blob: 3442b27f2f774858ce8025f18ccc43eae43c0aa4 [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 Bao0ff15de2019-03-20 11:26:06 -0700243UNZIP_PATTERN = ['IMAGES/*', 'META/*', '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"]
279 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "product_services",
280 "odm", "vendor", "system"]
281
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]
Tao Baobec89c12018-10-15 11:53:28 -0700520 common.RunAndCheckOutput(cmd)
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)
Tao Baobec89c12018-10-15 11:53:28 -0700562 common.RunAndCheckOutput(cmd)
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]
Tao Baobec89c12018-10-15 11:53:28 -0700586 common.RunAndCheckOutput(cmd)
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]
Tao Baobec89c12018-10-15 11:53:28 -0700601 common.RunAndCheckOutput(cmd)
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]
Tao Baobec89c12018-10-15 11:53:28 -0700609 common.RunAndCheckOutput(cmd)
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(
684 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
685 if os.path.exists(recovery_two_step_img_path):
686 recovery_two_step_img = common.GetBootableImage(
687 recovery_two_step_img_name, recovery_two_step_img_name,
688 OPTIONS.input_tmp, "RECOVERY")
689 common.ZipWriteStr(
690 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700691 logger.info(
692 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800693 script.WriteRawImage("/boot", recovery_two_step_img_name)
694 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700695 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800696 # The "recovery.img" entry has been written into package earlier.
697 script.WriteRawImage("/boot", "recovery.img")
698
699
Doug Zongkerc9253822014-02-04 12:17:58 -0800700def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700701 namelist = [name for name in target_files_zip.namelist()]
702 return ("SYSTEM/recovery-from-boot.p" in namelist or
703 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700704
Tao Bao457cbf62017-03-06 09:56:01 -0800705
Yifan Hong51d37562019-04-23 17:06:46 -0700706def HasPartition(target_files_zip, partition):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700707 try:
Yifan Hong51d37562019-04-23 17:06:46 -0700708 target_files_zip.getinfo(partition.upper() + "/")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700709 return True
710 except KeyError:
711 return False
712
Tao Bao457cbf62017-03-06 09:56:01 -0800713
Yifan Hong51d37562019-04-23 17:06:46 -0700714def HasVendorPartition(target_files_zip):
715 return HasPartition(target_files_zip, "vendor")
716
717
718def HasProductPartition(target_files_zip):
719 return HasPartition(target_files_zip, "product")
720
721
722def HasOdmPartition(target_files_zip):
723 return HasPartition(target_files_zip, "odm")
724
725
Tao Bao481bab82017-12-21 11:23:09 -0800726def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700727 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800728 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700729
730
Tao Bao481bab82017-12-21 11:23:09 -0800731def WriteFingerprintAssertion(script, target_info, source_info):
732 source_oem_props = source_info.oem_props
733 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700734
Tao Bao481bab82017-12-21 11:23:09 -0800735 if source_oem_props is None and target_oem_props is None:
736 script.AssertSomeFingerprint(
737 source_info.fingerprint, target_info.fingerprint)
738 elif source_oem_props is not None and target_oem_props is not None:
739 script.AssertSomeThumbprint(
740 target_info.GetBuildProp("ro.build.thumbprint"),
741 source_info.GetBuildProp("ro.build.thumbprint"))
742 elif source_oem_props is None and target_oem_props is not None:
743 script.AssertFingerprintOrThumbprint(
744 source_info.fingerprint,
745 target_info.GetBuildProp("ro.build.thumbprint"))
746 else:
747 script.AssertFingerprintOrThumbprint(
748 target_info.fingerprint,
749 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700750
Doug Zongkerfc44a512014-08-26 13:10:25 -0700751
Tao Bao481bab82017-12-21 11:23:09 -0800752def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
753 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700754 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700755
756 Metadata used for on-device compatibility verification is retrieved from
757 target_zip then added to compatibility.zip which is added to the output_zip
758 archive.
759
Tao Baobcd1d162017-08-26 13:10:26 -0700760 Compatibility archive should only be included for devices that have enabled
761 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700762
763 Args:
764 target_zip: Zip file containing the source files to be included for OTA.
765 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800766 target_info: The BuildInfo instance that holds the target build info.
767 source_info: The BuildInfo instance that holds the source build info, if
768 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700769 """
770
Yifan Hong51d37562019-04-23 17:06:46 -0700771 def AddCompatibilityArchive(framework_updated, device_updated):
772 """Adds compatibility info based on update status of both sides of Treble
773 boundary.
Tao Bao21803d32017-04-19 10:16:09 -0700774
Tao Baobcd1d162017-08-26 13:10:26 -0700775 Args:
Yifan Hong51d37562019-04-23 17:06:46 -0700776 framework_updated: If True, the system / product image will be updated
777 and therefore their metadata should be included.
778 device_updated: If True, the vendor / odm image will be updated and
779 therefore their metadata should be included.
Tao Baobcd1d162017-08-26 13:10:26 -0700780 """
781 # Determine what metadata we need. Files are names relative to META/.
782 compatibility_files = []
Yifan Hong51d37562019-04-23 17:06:46 -0700783 device_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
784 framework_metadata = ("system_manifest.xml", "system_matrix.xml")
785 if device_updated:
786 compatibility_files += device_metadata
787 if framework_updated:
788 compatibility_files += framework_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700789
Tao Baobcd1d162017-08-26 13:10:26 -0700790 # Create new archive.
791 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800792 compatibility_archive_zip = zipfile.ZipFile(
793 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700794
Tao Baobcd1d162017-08-26 13:10:26 -0700795 # Add metadata.
796 for file_name in compatibility_files:
797 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700798
Tao Baobcd1d162017-08-26 13:10:26 -0700799 if target_file_name in target_zip.namelist():
800 data = target_zip.read(target_file_name)
801 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700802
Tao Baobcd1d162017-08-26 13:10:26 -0700803 # Ensure files are written before we copy into output_zip.
804 compatibility_archive_zip.close()
805
806 # Only add the archive if we have any compatibility info.
807 if compatibility_archive_zip.namelist():
808 common.ZipWrite(output_zip, compatibility_archive.name,
809 arcname="compatibility.zip",
810 compress_type=zipfile.ZIP_STORED)
811
Yifan Hong51d37562019-04-23 17:06:46 -0700812 def FingerprintChanged(source_fp, target_fp):
813 if source_fp is None or target_fp is None:
814 return True
815 return source_fp != target_fp
816
Tao Baobcd1d162017-08-26 13:10:26 -0700817 # Will only proceed if the target has enabled the Treble support (as well as
818 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800819 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700820 return
821
xunchangabfa2652019-02-19 16:27:10 -0800822 # Skip adding the compatibility package as a workaround for b/114240221. The
823 # compatibility will always fail on devices without qualified kernels.
824 if OPTIONS.skip_compatibility_check:
825 return
826
Yifan Hong51d37562019-04-23 17:06:46 -0700827 # Full OTA carries the info for system/vendor/product/odm
Tao Bao481bab82017-12-21 11:23:09 -0800828 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700829 AddCompatibilityArchive(True, True)
830 return
831
Tao Bao481bab82017-12-21 11:23:09 -0800832 source_fp = source_info.fingerprint
833 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700834 system_updated = source_fp != target_fp
835
Yifan Hong51d37562019-04-23 17:06:46 -0700836 # other build fingerprints could be possibly blacklisted at build time. For
837 # such a case, we consider those images being changed.
838 vendor_updated = FingerprintChanged(source_info.vendor_fingerprint,
839 target_info.vendor_fingerprint)
840 product_updated = HasProductPartition(target_zip) and \
841 FingerprintChanged(source_info.product_fingerprint,
842 target_info.product_fingerprint)
843 odm_updated = HasOdmPartition(target_zip) and \
844 FingerprintChanged(source_info.odm_fingerprint,
845 target_info.odm_fingerprint)
Tao Baobcd1d162017-08-26 13:10:26 -0700846
Yifan Hong51d37562019-04-23 17:06:46 -0700847 AddCompatibilityArchive(system_updated or product_updated,
848 vendor_updated or odm_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700849
850
Tao Bao491d7e22018-02-21 13:17:22 -0800851def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800852 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700853
Tao Bao481bab82017-12-21 11:23:09 -0800854 # We don't know what version it will be installed on top of. We expect the API
855 # just won't change very often. Similarly for fstab, it might have changed in
856 # the target build.
857 target_api_version = target_info["recovery_api_version"]
858 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700859
Tao Bao481bab82017-12-21 11:23:09 -0800860 if target_info.oem_props and not OPTIONS.oem_no_mount:
861 target_info.WriteMountOemScript(script)
862
Tao Baodf3a48b2018-01-10 16:30:43 -0800863 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700864
Tao Bao491d7e22018-02-21 13:17:22 -0800865 if not OPTIONS.no_signing:
866 staging_file = common.MakeTempFile(suffix='.zip')
867 else:
868 staging_file = output_file
869
870 output_zip = zipfile.ZipFile(
871 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
872
Doug Zongker05d3dea2009-06-22 11:32:31 -0700873 device_specific = common.DeviceSpecificParams(
874 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800875 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700876 output_zip=output_zip,
877 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700878 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700879 metadata=metadata,
880 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700881
Tao Bao457cbf62017-03-06 09:56:01 -0800882 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800883
Tao Bao481bab82017-12-21 11:23:09 -0800884 # Assertions (e.g. downgrade check, device properties check).
885 ts = target_info.GetBuildProp("ro.build.date.utc")
886 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700887 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700888
Tao Bao481bab82017-12-21 11:23:09 -0800889 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700890 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800891
892 # Two-step package strategy (in chronological order, which is *not*
893 # the order in which the generated script has things):
894 #
895 # if stage is not "2/3" or "3/3":
896 # write recovery image to boot partition
897 # set stage to "2/3"
898 # reboot to boot partition and restart recovery
899 # else if stage is "2/3":
900 # write recovery image to recovery partition
901 # set stage to "3/3"
902 # reboot to recovery partition and restart recovery
903 # else:
904 # (stage must be "3/3")
905 # set stage to ""
906 # do normal full package installation:
907 # wipe and install system, boot image, etc.
908 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700909 # complete script normally
910 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800911
912 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
913 OPTIONS.input_tmp, "RECOVERY")
914 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800915 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800916 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800917 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800918 assert fs.fs_type.upper() == "EMMC", \
919 "two-step packages only supported on devices with EMMC /misc partitions"
920 bcb_dev = {"bcb_dev": fs.device}
921 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
922 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700923if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800924""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800925
926 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
927 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800928 script.WriteRawImage("/recovery", "recovery.img")
929 script.AppendExtra("""
930set_stage("%(bcb_dev)s", "3/3");
931reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700932else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800933""" % bcb_dev)
934
Tao Baod42e97e2016-11-30 12:11:57 -0800935 # Stage 3/3: Make changes.
936 script.Comment("Stage 3/3")
937
Tao Bao6c55a8a2015-04-08 15:30:27 -0700938 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800939 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700940
Doug Zongkere5ff5902012-01-17 10:55:37 -0800941 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700942
Doug Zongker01ce19c2014-02-04 13:48:15 -0800943 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700944
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700945 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800946 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700947 if HasVendorPartition(input_zip):
948 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700949
Doug Zongker4b9596f2014-06-09 14:15:45 -0700950 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800951
Yifan Hong10c530d2018-12-27 17:34:18 -0800952 def GetBlockDifference(partition):
953 # Full OTA is done as an "incremental" against an empty source image. This
954 # has the effect of writing new data from the package to the entire
955 # partition, but lets us reuse the updater code that writes incrementals to
956 # do it.
Yifan Hong8a66a712019-04-04 15:37:57 -0700957 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, input_zip,
958 info_dict=target_info,
959 reset_file_map=True)
Yifan Hong10c530d2018-12-27 17:34:18 -0800960 diff = common.BlockDifference(partition, tgt, src=None)
961 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700962
Yifan Hong10c530d2018-12-27 17:34:18 -0800963 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
964 if device_specific_diffs:
965 assert all(isinstance(diff, common.BlockDifference)
966 for diff in device_specific_diffs), \
967 "FullOTA_GetBlockDifferences is not returning a list of " \
968 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800969
Yifan Hong10c530d2018-12-27 17:34:18 -0800970 progress_dict = dict()
971 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700972 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800973 block_diffs.append(GetBlockDifference("vendor"))
974 progress_dict["vendor"] = 0.1
975 if device_specific_diffs:
976 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700977
Yifan Hong10c530d2018-12-27 17:34:18 -0800978 if target_info.get('use_dynamic_partitions') == "true":
979 # Use empty source_info_dict to indicate that all partitions / groups must
980 # be re-added.
981 dynamic_partitions_diff = common.DynamicPartitionsDifference(
982 info_dict=OPTIONS.info_dict,
983 block_diffs=block_diffs,
984 progress_dict=progress_dict)
985 dynamic_partitions_diff.WriteScript(script, output_zip,
986 write_verify_script=OPTIONS.verify)
987 else:
988 for block_diff in block_diffs:
989 block_diff.WriteScript(script, output_zip,
990 progress=progress_dict.get(block_diff.partition),
991 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700992
Tao Bao481bab82017-12-21 11:23:09 -0800993 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700994
Yifan Hong10c530d2018-12-27 17:34:18 -0800995 boot_img = common.GetBootableImage(
996 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800997 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700998 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700999
Doug Zongker01ce19c2014-02-04 13:48:15 -08001000 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -07001001 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -07001002
Doug Zongker01ce19c2014-02-04 13:48:15 -08001003 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001004 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -07001005
Doug Zongker1c390a22009-05-14 19:06:36 -07001006 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001007 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001008
Doug Zongker14833602010-02-02 13:12:04 -08001009 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001010
Doug Zongker922206e2014-03-04 13:16:24 -08001011 if OPTIONS.wipe_user_data:
1012 script.ShowProgress(0.1, 10)
1013 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001014
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001015 if OPTIONS.two_step:
1016 script.AppendExtra("""
1017set_stage("%(bcb_dev)s", "");
1018""" % bcb_dev)
1019 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -08001020
1021 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
1022 script.Comment("Stage 1/3")
1023 _WriteRecoveryImageToBoot(script, output_zip)
1024
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001025 script.AppendExtra("""
1026set_stage("%(bcb_dev)s", "2/3");
1027reboot_now("%(bcb_dev)s", "");
1028endif;
1029endif;
1030""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -08001031
Tao Bao5d182562016-02-23 11:38:39 -08001032 script.SetProgress(1)
1033 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001034 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001035
1036 # We haven't written the metadata entry, which will be done in
1037 # FinalizeMetadata.
1038 common.ZipClose(output_zip)
1039
1040 needed_property_files = (
1041 NonAbOtaPropertyFiles(),
1042 )
1043 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -07001044
Doug Zongkerfc44a512014-08-26 13:10:25 -07001045
xunchang1cfe2512019-02-19 14:14:48 -08001046def WriteMetadata(metadata, output):
1047 """Writes the metadata to the zip archive or a file.
1048
1049 Args:
1050 metadata: The metadata dict for the package.
1051 output: A ZipFile object or a string of the output file path.
1052 """
1053
Tao Bao59cf0c52019-06-25 10:04:24 -07001054 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())])
xunchang1cfe2512019-02-19 14:14:48 -08001055 if isinstance(output, zipfile.ZipFile):
1056 common.ZipWriteStr(output, METADATA_NAME, value,
1057 compress_type=zipfile.ZIP_STORED)
1058 return
1059
1060 with open(output, 'w') as f:
1061 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001062
Doug Zongkerfc44a512014-08-26 13:10:25 -07001063
Tao Bao481bab82017-12-21 11:23:09 -08001064def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001065 # Only incremental OTAs are allowed to reach here.
1066 assert OPTIONS.incremental_source is not None
1067
Tao Bao481bab82017-12-21 11:23:09 -08001068 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1069 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Bao59cf0c52019-06-25 10:04:24 -07001070 is_downgrade = int(post_timestamp) < int(pre_timestamp)
Tao Baob31892e2017-02-07 11:21:17 -08001071
1072 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001073 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001074 raise RuntimeError(
1075 "--downgrade or --override_timestamp specified but no downgrade "
1076 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001077 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001078 else:
1079 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001080 raise RuntimeError(
1081 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1082 "Need to specify --override_timestamp OR --downgrade to allow "
1083 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001084
1085
Tao Baodf3a48b2018-01-10 16:30:43 -08001086def GetPackageMetadata(target_info, source_info=None):
1087 """Generates and returns the metadata dict.
1088
1089 It generates a dict() that contains the info to be written into an OTA
1090 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001091 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001092
1093 Args:
1094 target_info: The BuildInfo instance that holds the target build info.
1095 source_info: The BuildInfo instance that holds the source build info, or
1096 None if generating full OTA.
1097
1098 Returns:
1099 A dict to be written into package metadata entry.
1100 """
1101 assert isinstance(target_info, BuildInfo)
1102 assert source_info is None or isinstance(source_info, BuildInfo)
1103
1104 metadata = {
1105 'post-build' : target_info.fingerprint,
1106 'post-build-incremental' : target_info.GetBuildProp(
1107 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001108 'post-sdk-level' : target_info.GetBuildProp(
1109 'ro.build.version.sdk'),
1110 'post-security-patch-level' : target_info.GetBuildProp(
1111 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001112 }
1113
1114 if target_info.is_ab:
1115 metadata['ota-type'] = 'AB'
1116 metadata['ota-required-cache'] = '0'
1117 else:
1118 metadata['ota-type'] = 'BLOCK'
1119
1120 if OPTIONS.wipe_user_data:
1121 metadata['ota-wipe'] = 'yes'
1122
Tao Bao393eeb42019-03-06 16:00:38 -08001123 if OPTIONS.retrofit_dynamic_partitions:
1124 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1125
Tao Baodf3a48b2018-01-10 16:30:43 -08001126 is_incremental = source_info is not None
1127 if is_incremental:
1128 metadata['pre-build'] = source_info.fingerprint
1129 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1130 'ro.build.version.incremental')
1131 metadata['pre-device'] = source_info.device
1132 else:
1133 metadata['pre-device'] = target_info.device
1134
Tao Baofaa8e0b2018-04-12 14:31:43 -07001135 # Use the actual post-timestamp, even for a downgrade case.
1136 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1137
1138 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001139 if is_incremental:
1140 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001141
1142 return metadata
1143
1144
Tao Baod3fc38a2018-03-08 16:09:01 -08001145class PropertyFiles(object):
1146 """A class that computes the property-files string for an OTA package.
1147
1148 A property-files string is a comma-separated string that contains the
1149 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1150 can be fetched directly with the package URL along with the offset/size info.
1151 These strings can be used for streaming A/B OTAs, or allowing an updater to
1152 download package metadata entry directly, without paying the cost of
1153 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001154
Tao Baocc8e2662018-03-01 19:30:00 -08001155 Computing the final property-files string requires two passes. Because doing
1156 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1157 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1158 values.
1159
1160 This class provides functions to be called for each pass. The general flow is
1161 as follows.
1162
Tao Baod3fc38a2018-03-08 16:09:01 -08001163 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001164 # The first pass, which writes placeholders before doing initial signing.
1165 property_files.Compute()
1166 SignOutput()
1167
1168 # The second pass, by replacing the placeholders with actual data.
1169 property_files.Finalize()
1170 SignOutput()
1171
1172 And the caller can additionally verify the final result.
1173
1174 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001175 """
1176
Tao Baocc8e2662018-03-01 19:30:00 -08001177 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001178 self.name = None
1179 self.required = ()
1180 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001181
Tao Baocc8e2662018-03-01 19:30:00 -08001182 def Compute(self, input_zip):
1183 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001184
Tao Baocc8e2662018-03-01 19:30:00 -08001185 We reserve extra space for the offset and size of the metadata entry itself,
1186 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001187
Tao Baocc8e2662018-03-01 19:30:00 -08001188 Args:
1189 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001190
Tao Baocc8e2662018-03-01 19:30:00 -08001191 Returns:
1192 A string with placeholders for the metadata offset/size info, e.g.
1193 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1194 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001195 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001196
Tao Baod2ce2ed2018-03-16 12:59:42 -07001197 class InsufficientSpaceException(Exception):
1198 pass
1199
Tao Baocc8e2662018-03-01 19:30:00 -08001200 def Finalize(self, input_zip, reserved_length):
1201 """Finalizes a property-files string with actual METADATA offset/size info.
1202
1203 The input ZIP file has been signed, with the ZIP entries in the desired
1204 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1205 the ZIP entry offsets and construct the property-files string with actual
1206 data. Note that during this process, we must pad the property-files string
1207 to the reserved length, so that the METADATA entry size remains the same.
1208 Otherwise the entries' offsets and sizes may change again.
1209
1210 Args:
1211 input_zip: The input ZIP file.
1212 reserved_length: The reserved length of the property-files string during
1213 the call to Compute(). The final string must be no more than this
1214 size.
1215
1216 Returns:
1217 A property-files string including the metadata offset/size info, e.g.
1218 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1219
1220 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001221 InsufficientSpaceException: If the reserved length is insufficient to hold
1222 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001223 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001224 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001225 if len(result) > reserved_length:
1226 raise self.InsufficientSpaceException(
1227 'Insufficient reserved space: reserved={}, actual={}'.format(
1228 reserved_length, len(result)))
1229
Tao Baocc8e2662018-03-01 19:30:00 -08001230 result += ' ' * (reserved_length - len(result))
1231 return result
1232
1233 def Verify(self, input_zip, expected):
1234 """Verifies the input ZIP file contains the expected property-files string.
1235
1236 Args:
1237 input_zip: The input ZIP file.
1238 expected: The property-files string that's computed from Finalize().
1239
1240 Raises:
1241 AssertionError: On finding a mismatch.
1242 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001243 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001244 assert actual == expected, \
1245 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1246
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001247 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1248 """
1249 Constructs the property-files string per request.
1250
1251 Args:
1252 zip_file: The input ZIP file.
1253 reserved_length: The reserved length of the property-files string.
1254
1255 Returns:
1256 A property-files string including the metadata offset/size info, e.g.
1257 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1258 """
Tao Baocc8e2662018-03-01 19:30:00 -08001259
1260 def ComputeEntryOffsetSize(name):
1261 """Computes the zip entry offset and size."""
1262 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001263 offset = info.header_offset
1264 offset += zipfile.sizeFileHeader
1265 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001266 size = info.file_size
1267 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1268
1269 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001270 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001271 for entry in self.required:
1272 tokens.append(ComputeEntryOffsetSize(entry))
1273 for entry in self.optional:
1274 if entry in zip_file.namelist():
1275 tokens.append(ComputeEntryOffsetSize(entry))
1276
1277 # 'META-INF/com/android/metadata' is required. We don't know its actual
1278 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001279 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1280 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1281 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1282 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001283 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001284 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001285 else:
1286 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1287
1288 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001289
Tao Bao85f16982018-03-08 16:28:33 -08001290 def _GetPrecomputed(self, input_zip):
1291 """Computes the additional tokens to be included into the property-files.
1292
1293 This applies to tokens without actual ZIP entries, such as
1294 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1295 that they can download the payload metadata directly with the info.
1296
1297 Args:
1298 input_zip: The input zip file.
1299
1300 Returns:
1301 A list of strings (tokens) to be added to the property-files string.
1302 """
1303 # pylint: disable=no-self-use
1304 # pylint: disable=unused-argument
1305 return []
1306
Tao Baofe5b69a2018-03-02 09:47:43 -08001307
Tao Baod3fc38a2018-03-08 16:09:01 -08001308class StreamingPropertyFiles(PropertyFiles):
1309 """A subclass for computing the property-files for streaming A/B OTAs."""
1310
1311 def __init__(self):
1312 super(StreamingPropertyFiles, self).__init__()
1313 self.name = 'ota-streaming-property-files'
1314 self.required = (
1315 # payload.bin and payload_properties.txt must exist.
1316 'payload.bin',
1317 'payload_properties.txt',
1318 )
1319 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001320 # care_map is available only if dm-verity is enabled.
1321 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001322 'care_map.txt',
1323 # compatibility.zip is available only if target supports Treble.
1324 'compatibility.zip',
1325 )
1326
1327
Tao Bao85f16982018-03-08 16:28:33 -08001328class AbOtaPropertyFiles(StreamingPropertyFiles):
1329 """The property-files for A/B OTA that includes payload_metadata.bin info.
1330
1331 Since P, we expose one more token (aka property-file), in addition to the ones
1332 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1333 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1334 doesn't exist as a separate ZIP entry, but can be used to verify if the
1335 payload can be applied on the given device.
1336
1337 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1338 and the newly added 'ota-property-files' in P. The new token will only be
1339 available in 'ota-property-files'.
1340 """
1341
1342 def __init__(self):
1343 super(AbOtaPropertyFiles, self).__init__()
1344 self.name = 'ota-property-files'
1345
1346 def _GetPrecomputed(self, input_zip):
1347 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1348 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1349
1350 @staticmethod
1351 def _GetPayloadMetadataOffsetAndSize(input_zip):
1352 """Computes the offset and size of the payload metadata for a given package.
1353
1354 (From system/update_engine/update_metadata.proto)
1355 A delta update file contains all the deltas needed to update a system from
1356 one specific version to another specific version. The update format is
1357 represented by this struct pseudocode:
1358
1359 struct delta_update_file {
1360 char magic[4] = "CrAU";
1361 uint64 file_format_version;
1362 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1363
1364 // Only present if format_version > 1:
1365 uint32 metadata_signature_size;
1366
1367 // The Bzip2 compressed DeltaArchiveManifest
1368 char manifest[metadata_signature_size];
1369
1370 // The signature of the metadata (from the beginning of the payload up to
1371 // this location, not including the signature itself). This is a
1372 // serialized Signatures message.
1373 char medatada_signature_message[metadata_signature_size];
1374
1375 // Data blobs for files, no specific format. The specific offset
1376 // and length of each data blob is recorded in the DeltaArchiveManifest.
1377 struct {
1378 char data[];
1379 } blobs[];
1380
1381 // These two are not signed:
1382 uint64 payload_signatures_message_size;
1383 char payload_signatures_message[];
1384 };
1385
1386 'payload-metadata.bin' contains all the bytes from the beginning of the
1387 payload, till the end of 'medatada_signature_message'.
1388 """
1389 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001390 payload_offset = payload_info.header_offset
1391 payload_offset += zipfile.sizeFileHeader
1392 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001393 payload_size = payload_info.file_size
1394
Tao Bao59cf0c52019-06-25 10:04:24 -07001395 with input_zip.open('payload.bin') as payload_fp:
Tao Bao85f16982018-03-08 16:28:33 -08001396 header_bin = payload_fp.read(24)
1397
1398 # network byte order (big-endian)
1399 header = struct.unpack("!IQQL", header_bin)
1400
1401 # 'CrAU'
1402 magic = header[0]
1403 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1404
1405 manifest_size = header[2]
1406 metadata_signature_size = header[3]
1407 metadata_total = 24 + manifest_size + metadata_signature_size
1408 assert metadata_total < payload_size
1409
1410 return (payload_offset, metadata_total)
1411
1412
Tao Bao491d7e22018-02-21 13:17:22 -08001413class NonAbOtaPropertyFiles(PropertyFiles):
1414 """The property-files for non-A/B OTA.
1415
1416 For non-A/B OTA, the property-files string contains the info for METADATA
1417 entry, with which a system updater can be fetched the package metadata prior
1418 to downloading the entire package.
1419 """
1420
1421 def __init__(self):
1422 super(NonAbOtaPropertyFiles, self).__init__()
1423 self.name = 'ota-property-files'
1424
1425
Tao Baod3fc38a2018-03-08 16:09:01 -08001426def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001427 """Finalizes the metadata and signs an A/B OTA package.
1428
1429 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1430 that contains the offsets and sizes for the ZIP entries. An example
1431 property-files string is as follows.
1432
1433 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1434
1435 OTA server can pass down this string, in addition to the package URL, to the
1436 system update client. System update client can then fetch individual ZIP
1437 entries (ZIP_STORED) directly at the given offset of the URL.
1438
1439 Args:
1440 metadata: The metadata dict for the package.
1441 input_file: The input ZIP filename that doesn't contain the package METADATA
1442 entry yet.
1443 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001444 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001445 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001446
Tao Baod2ce2ed2018-03-16 12:59:42 -07001447 def ComputeAllPropertyFiles(input_file, needed_property_files):
1448 # Write the current metadata entry with placeholders.
1449 with zipfile.ZipFile(input_file) as input_zip:
1450 for property_files in needed_property_files:
1451 metadata[property_files.name] = property_files.Compute(input_zip)
1452 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001453
Tao Baod2ce2ed2018-03-16 12:59:42 -07001454 if METADATA_NAME in namelist:
1455 common.ZipDelete(input_file, METADATA_NAME)
1456 output_zip = zipfile.ZipFile(input_file, 'a')
1457 WriteMetadata(metadata, output_zip)
1458 common.ZipClose(output_zip)
1459
1460 if OPTIONS.no_signing:
1461 return input_file
1462
Tao Bao491d7e22018-02-21 13:17:22 -08001463 prelim_signing = common.MakeTempFile(suffix='.zip')
1464 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001465 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001466
Tao Baod2ce2ed2018-03-16 12:59:42 -07001467 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1468 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1469 for property_files in needed_property_files:
1470 metadata[property_files.name] = property_files.Finalize(
1471 prelim_signing_zip, len(metadata[property_files.name]))
1472
1473 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1474 # entries, as well as padding the entry headers. We do a preliminary signing
1475 # (with an incomplete metadata entry) to allow that to happen. Then compute
1476 # the ZIP entry offsets, write back the final metadata and do the final
1477 # signing.
1478 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1479 try:
1480 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1481 except PropertyFiles.InsufficientSpaceException:
1482 # Even with the preliminary signing, the entry orders may change
1483 # dramatically, which leads to insufficiently reserved space during the
1484 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1485 # preliminary signing works, based on the already ordered ZIP entries, to
1486 # address the issue.
1487 prelim_signing = ComputeAllPropertyFiles(
1488 prelim_signing, needed_property_files)
1489 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001490
1491 # Replace the METADATA entry.
1492 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001493 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001494 WriteMetadata(metadata, output_zip)
1495 common.ZipClose(output_zip)
1496
1497 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001498 if OPTIONS.no_signing:
1499 output_file = prelim_signing
1500 else:
1501 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001502
1503 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001504 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001505 for property_files in needed_property_files:
1506 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001507
xunchang1cfe2512019-02-19 14:14:48 -08001508 # If requested, dump the metadata to a separate file.
1509 output_metadata_path = OPTIONS.output_metadata_path
1510 if output_metadata_path:
1511 WriteMetadata(metadata, output_metadata_path)
1512
Tao Baofe5b69a2018-03-02 09:47:43 -08001513
Tao Bao491d7e22018-02-21 13:17:22 -08001514def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001515 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1516 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001517
Tao Bao481bab82017-12-21 11:23:09 -08001518 target_api_version = target_info["recovery_api_version"]
1519 source_api_version = source_info["recovery_api_version"]
1520 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001521 logger.warning(
1522 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001523
Tao Bao481bab82017-12-21 11:23:09 -08001524 script = edify_generator.EdifyGenerator(
1525 source_api_version, target_info, fstab=source_info["fstab"])
1526
1527 if target_info.oem_props or source_info.oem_props:
1528 if not OPTIONS.oem_no_mount:
1529 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001530
Tao Baodf3a48b2018-01-10 16:30:43 -08001531 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001532
Tao Bao491d7e22018-02-21 13:17:22 -08001533 if not OPTIONS.no_signing:
1534 staging_file = common.MakeTempFile(suffix='.zip')
1535 else:
1536 staging_file = output_file
1537
1538 output_zip = zipfile.ZipFile(
1539 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1540
Geremy Condra36bd3652014-02-06 19:45:10 -08001541 device_specific = common.DeviceSpecificParams(
1542 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001543 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001544 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001545 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001546 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001547 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001548 output_zip=output_zip,
1549 script=script,
1550 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001551 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001552
Geremy Condra36bd3652014-02-06 19:45:10 -08001553 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001554 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001555 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001556 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001557 updating_boot = (not OPTIONS.two_step and
1558 (source_boot.data != target_boot.data))
1559
Geremy Condra36bd3652014-02-06 19:45:10 -08001560 target_recovery = common.GetBootableImage(
1561 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001562
Yifan Hong8a66a712019-04-04 15:37:57 -07001563 # See notes in common.GetUserImage()
Tao Baoe709b092018-02-07 12:40:00 -08001564 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1565 target_info.get('ext4_share_dup_blocks') == "true")
Yifan Hong8a66a712019-04-04 15:37:57 -07001566 system_src = common.GetUserImage("system", OPTIONS.source_tmp, source_zip,
1567 info_dict=source_info,
1568 allow_shared_blocks=allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001569
1570 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1571 "system", 4096, target_info)
Yifan Hong8a66a712019-04-04 15:37:57 -07001572 system_tgt = common.GetUserImage("system", OPTIONS.target_tmp, target_zip,
1573 info_dict=target_info,
1574 allow_shared_blocks=allow_shared_blocks,
1575 hashtree_info_generator=
1576 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001577
Tao Bao0582cb62017-12-21 11:47:01 -08001578 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001579 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001580 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001581
Tao Baof8acad12016-07-07 09:09:58 -07001582 # Check the first block of the source system partition for remount R/W only
1583 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001584 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001585 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001586 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1587 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1588 # b) the blocks listed in block map may not contain all the bytes for a given
1589 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001590 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001591 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1592 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001593 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001594 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001595 version=blockimgdiff_version,
1596 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001597
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001598 if HasVendorPartition(target_zip):
1599 if not HasVendorPartition(source_zip):
1600 raise RuntimeError("can't generate incremental that adds /vendor")
Yifan Hong8a66a712019-04-04 15:37:57 -07001601 vendor_src = common.GetUserImage("vendor", OPTIONS.source_tmp, source_zip,
1602 info_dict=source_info,
1603 allow_shared_blocks=allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001604 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1605 "vendor", 4096, target_info)
Yifan Hong8a66a712019-04-04 15:37:57 -07001606 vendor_tgt = common.GetUserImage(
1607 "vendor", OPTIONS.target_tmp, target_zip,
1608 info_dict=target_info,
1609 allow_shared_blocks=allow_shared_blocks,
1610 hashtree_info_generator=hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001611
1612 # Check first block of vendor partition for remount R/W only if
1613 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001614 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001615 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001616 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001617 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001618 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001619 version=blockimgdiff_version,
1620 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001621 else:
1622 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001623
Tao Baobcd1d162017-08-26 13:10:26 -07001624 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001625 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001626
Tao Bao481bab82017-12-21 11:23:09 -08001627 # Assertions (e.g. device properties check).
1628 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001629 device_specific.IncrementalOTA_Assertions()
1630
1631 # Two-step incremental package strategy (in chronological order,
1632 # which is *not* the order in which the generated script has
1633 # things):
1634 #
1635 # if stage is not "2/3" or "3/3":
1636 # do verification on current system
1637 # write recovery image to boot partition
1638 # set stage to "2/3"
1639 # reboot to boot partition and restart recovery
1640 # else if stage is "2/3":
1641 # write recovery image to recovery partition
1642 # set stage to "3/3"
1643 # reboot to recovery partition and restart recovery
1644 # else:
1645 # (stage must be "3/3")
1646 # perform update:
1647 # patch system files, etc.
1648 # force full install of new boot image
1649 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001650 # complete script normally
1651 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001652
1653 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001654 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001655 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001656 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001657 assert fs.fs_type.upper() == "EMMC", \
1658 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001659 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001660 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1661 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001662if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001663""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001664
1665 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1666 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001667 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001668 script.WriteRawImage("/recovery", "recovery.img")
1669 script.AppendExtra("""
1670set_stage("%(bcb_dev)s", "3/3");
1671reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001672else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001673""" % bcb_dev)
1674
Tao Baod42e97e2016-11-30 12:11:57 -08001675 # Stage 1/3: (a) Verify the current system.
1676 script.Comment("Stage 1/3")
1677
Tao Bao6c55a8a2015-04-08 15:30:27 -07001678 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001679 script.Print("Source: {}".format(source_info.fingerprint))
1680 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001681
Geremy Condra36bd3652014-02-06 19:45:10 -08001682 script.Print("Verifying current system...")
1683
1684 device_specific.IncrementalOTA_VerifyBegin()
1685
Tao Bao481bab82017-12-21 11:23:09 -08001686 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001687
Tao Baod8d14be2016-02-04 14:26:02 -08001688 # Check the required cache size (i.e. stashed blocks).
1689 size = []
1690 if system_diff:
1691 size.append(system_diff.required_cache)
1692 if vendor_diff:
1693 size.append(vendor_diff.required_cache)
1694
Geremy Condra36bd3652014-02-06 19:45:10 -08001695 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001696 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001697 d = common.Difference(target_boot, source_boot)
1698 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001699 if d is None:
1700 include_full_boot = True
1701 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1702 else:
1703 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001704
Tao Bao32fcdab2018-10-12 10:30:39 -07001705 logger.info(
1706 "boot target: %d source: %d diff: %d", target_boot.size,
1707 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001708
Tao Bao51216552018-08-26 11:53:15 -07001709 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001710
Tao Bao51216552018-08-26 11:53:15 -07001711 script.PatchPartitionCheck(
1712 "{}:{}:{}:{}".format(
1713 boot_type, boot_device, target_boot.size, target_boot.sha1),
1714 "{}:{}:{}:{}".format(
1715 boot_type, boot_device, source_boot.size, source_boot.sha1))
1716
Tao Baod8d14be2016-02-04 14:26:02 -08001717 size.append(target_boot.size)
1718
1719 if size:
1720 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001721
1722 device_specific.IncrementalOTA_VerifyEnd()
1723
1724 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001725 # Stage 1/3: (b) Write recovery image to /boot.
1726 _WriteRecoveryImageToBoot(script, output_zip)
1727
Geremy Condra36bd3652014-02-06 19:45:10 -08001728 script.AppendExtra("""
1729set_stage("%(bcb_dev)s", "2/3");
1730reboot_now("%(bcb_dev)s", "");
1731else
1732""" % bcb_dev)
1733
Tao Baod42e97e2016-11-30 12:11:57 -08001734 # Stage 3/3: Make changes.
1735 script.Comment("Stage 3/3")
1736
Jesse Zhao75bcea02015-01-06 10:59:53 -08001737 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001738 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001739 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001740 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001741 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1742 if device_specific_diffs:
1743 assert all(isinstance(diff, common.BlockDifference)
1744 for diff in device_specific_diffs), \
1745 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1746 "BlockDifference objects"
1747 for diff in device_specific_diffs:
1748 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001749
Geremy Condra36bd3652014-02-06 19:45:10 -08001750 script.Comment("---- start making changes here ----")
1751
1752 device_specific.IncrementalOTA_InstallBegin()
1753
Yifan Hong10c530d2018-12-27 17:34:18 -08001754 block_diffs = [system_diff]
1755 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001756 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001757 block_diffs.append(vendor_diff)
1758 progress_dict["vendor"] = 0.1
1759 if device_specific_diffs:
1760 block_diffs += device_specific_diffs
1761
1762 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1763 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1764 raise RuntimeError(
1765 "can't generate incremental that disables dynamic partitions")
1766 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1767 info_dict=OPTIONS.target_info_dict,
1768 source_info_dict=OPTIONS.source_info_dict,
1769 block_diffs=block_diffs,
1770 progress_dict=progress_dict)
1771 dynamic_partitions_diff.WriteScript(
1772 script, output_zip, write_verify_script=OPTIONS.verify)
1773 else:
1774 for block_diff in block_diffs:
1775 block_diff.WriteScript(script, output_zip,
1776 progress=progress_dict.get(block_diff.partition),
1777 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001778
1779 if OPTIONS.two_step:
1780 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1781 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001782 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001783
1784 if not OPTIONS.two_step:
1785 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001786 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001787 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001788 script.Print("Installing boot image...")
1789 script.WriteRawImage("/boot", "boot.img")
1790 else:
1791 # Produce the boot image by applying a patch to the current
1792 # contents of the boot partition, and write it back to the
1793 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001794 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001795 script.Print("Patching boot image...")
1796 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001797 script.PatchPartition(
1798 '{}:{}:{}:{}'.format(
1799 boot_type, boot_device, target_boot.size, target_boot.sha1),
1800 '{}:{}:{}:{}'.format(
1801 boot_type, boot_device, source_boot.size, source_boot.sha1),
1802 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001803 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001804 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001805
1806 # Do device-specific installation (eg, write radio image).
1807 device_specific.IncrementalOTA_InstallEnd()
1808
1809 if OPTIONS.extra_script is not None:
1810 script.AppendExtra(OPTIONS.extra_script)
1811
Doug Zongker922206e2014-03-04 13:16:24 -08001812 if OPTIONS.wipe_user_data:
1813 script.Print("Erasing user data...")
1814 script.FormatPartition("/data")
1815
Geremy Condra36bd3652014-02-06 19:45:10 -08001816 if OPTIONS.two_step:
1817 script.AppendExtra("""
1818set_stage("%(bcb_dev)s", "");
1819endif;
1820endif;
1821""" % bcb_dev)
1822
1823 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001824 # For downgrade OTAs, we prefer to use the update-binary in the source
1825 # build that is actually newer than the one in the target build.
1826 if OPTIONS.downgrade:
1827 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1828 else:
1829 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001830 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001831
1832 # We haven't written the metadata entry yet, which will be handled in
1833 # FinalizeMetadata().
1834 common.ZipClose(output_zip)
1835
1836 # Sign the generated zip package unless no_signing is specified.
1837 needed_property_files = (
1838 NonAbOtaPropertyFiles(),
1839 )
1840 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001841
Doug Zongker32b527d2014-03-04 10:03:02 -08001842
Tao Bao15a146a2018-02-21 16:06:59 -08001843def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001844 """Returns a target-files.zip file for generating secondary payload.
1845
1846 Although the original target-files.zip already contains secondary slot
1847 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1848 ones without _other suffix. Note that we cannot instead modify the names in
1849 META/ab_partitions.txt, because there are no matching partitions on device.
1850
1851 For the partitions that don't have secondary images, the ones for primary
1852 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1853 bootloader images in the inactive slot.
1854
1855 Args:
1856 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001857 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001858
1859 Returns:
1860 The filename of the target-files.zip for generating secondary payload.
1861 """
1862 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1863 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1864
Tao Baodba59ee2018-01-09 13:21:02 -08001865 with zipfile.ZipFile(input_file, 'r') as input_zip:
1866 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001867
Tao Bao0ff15de2019-03-20 11:26:06 -07001868 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001869 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001870 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1871 if info.filename == 'IMAGES/system_other.img':
1872 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1873
1874 # Primary images and friends need to be skipped explicitly.
1875 elif info.filename in ('IMAGES/system.img',
1876 'IMAGES/system.map'):
1877 pass
1878
Tao Bao15a146a2018-02-21 16:06:59 -08001879 # Skip copying the postinstall config if requested.
1880 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1881 pass
1882
Tao Bao12489802018-07-12 14:47:38 -07001883 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001884 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1885
Tao Baof7140c02018-01-30 17:09:24 -08001886 common.ZipClose(target_zip)
1887
1888 return target_file
1889
1890
Tao Bao15a146a2018-02-21 16:06:59 -08001891def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1892 """Returns a target-files.zip that's not containing postinstall_config.txt.
1893
1894 This allows brillo_update_payload script to skip writing all the postinstall
1895 hooks in the generated payload. The input target-files.zip file will be
1896 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1897 contain the postinstall_config.txt entry, the input file will be returned.
1898
1899 Args:
1900 input_file: The input target-files.zip filename.
1901
1902 Returns:
1903 The filename of target-files.zip that doesn't contain postinstall config.
1904 """
1905 # We should only make a copy if postinstall_config entry exists.
1906 with zipfile.ZipFile(input_file, 'r') as input_zip:
1907 if POSTINSTALL_CONFIG not in input_zip.namelist():
1908 return input_file
1909
1910 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1911 shutil.copyfile(input_file, target_file)
1912 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1913 return target_file
1914
1915
Yifan Hong50e79542018-11-08 17:44:12 -08001916def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001917 super_block_devices,
1918 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001919 """Returns a target-files.zip for retrofitting dynamic partitions.
1920
1921 This allows brillo_update_payload to generate an OTA based on the exact
1922 bits on the block devices. Postinstall is disabled.
1923
1924 Args:
1925 input_file: The input target-files.zip filename.
1926 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001927 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001928
1929 Returns:
1930 The filename of target-files.zip with *.img replaced with super_*.img for
1931 each block device in super_block_devices.
1932 """
1933 assert super_block_devices, "No super_block_devices are specified."
1934
1935 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001936 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001937
1938 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1939 shutil.copyfile(input_file, target_file)
1940
1941 with zipfile.ZipFile(input_file, 'r') as input_zip:
1942 namelist = input_zip.namelist()
1943
Yifan Hongb433eba2019-03-06 12:42:53 -08001944 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1945
1946 # Remove partitions from META/ab_partitions.txt that is in
1947 # dynamic_partition_list but not in super_block_devices so that
1948 # brillo_update_payload won't generate update for those logical partitions.
1949 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1950 with open(ab_partitions_file) as f:
1951 ab_partitions_lines = f.readlines()
1952 ab_partitions = [line.strip() for line in ab_partitions_lines]
1953 # Assert that all super_block_devices are in ab_partitions
1954 super_device_not_updated = [partition for partition in super_block_devices
1955 if partition not in ab_partitions]
1956 assert not super_device_not_updated, \
1957 "{} is in super_block_devices but not in {}".format(
1958 super_device_not_updated, AB_PARTITIONS)
1959 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1960 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1961 with open(new_ab_partitions, 'w') as f:
1962 for partition in ab_partitions:
1963 if (partition in dynamic_partition_list and
1964 partition not in super_block_devices):
Tao Bao59cf0c52019-06-25 10:04:24 -07001965 logger.info("Dropping %s from ab_partitions.txt", partition)
1966 continue
Yifan Hongb433eba2019-03-06 12:42:53 -08001967 f.write(partition + "\n")
1968 to_delete = [AB_PARTITIONS]
1969
Yifan Hong50e79542018-11-08 17:44:12 -08001970 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001971 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001972
1973 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1974 # is a regular update on devices without dynamic partitions support.
1975 to_delete += [DYNAMIC_PARTITION_INFO]
1976
Tao Bao03fecb62018-11-28 10:59:23 -08001977 # Remove the existing partition images as well as the map files.
Tao Bao59cf0c52019-06-25 10:04:24 -07001978 to_delete += list(replace.values())
Tao Bao03fecb62018-11-28 10:59:23 -08001979 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001980
1981 common.ZipDelete(target_file, to_delete)
1982
Yifan Hong50e79542018-11-08 17:44:12 -08001983 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1984
1985 # Write super_{foo}.img as {foo}.img.
1986 for src, dst in replace.items():
1987 assert src in namelist, \
Tao Bao59cf0c52019-06-25 10:04:24 -07001988 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
Yifan Hong50e79542018-11-08 17:44:12 -08001989 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1990 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1991
Yifan Hongb433eba2019-03-06 12:42:53 -08001992 # Write new ab_partitions.txt file
1993 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1994
Yifan Hong50e79542018-11-08 17:44:12 -08001995 common.ZipClose(target_zip)
1996
1997 return target_file
1998
1999
Tao Baoc098e9e2016-01-07 13:03:56 -08002000def WriteABOTAPackageWithBrilloScript(target_file, output_file,
2001 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08002002 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07002003 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08002004 if not OPTIONS.no_signing:
2005 staging_file = common.MakeTempFile(suffix='.zip')
2006 else:
2007 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08002008 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08002009 compression=zipfile.ZIP_DEFLATED)
2010
Tao Bao481bab82017-12-21 11:23:09 -08002011 if source_file is not None:
2012 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
2013 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
2014 else:
2015 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
2016 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08002017
Tao Bao481bab82017-12-21 11:23:09 -08002018 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08002019 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08002020
Yifan Hong50e79542018-11-08 17:44:12 -08002021 if OPTIONS.retrofit_dynamic_partitions:
2022 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08002023 target_file, target_info.get("super_block_devices").strip().split(),
2024 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08002025 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08002026 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
2027
Tao Bao40b18822018-01-30 18:19:04 -08002028 # Generate payload.
2029 payload = Payload()
2030
2031 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07002032 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08002033 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07002034 else:
2035 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08002036 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08002037
Tao Bao40b18822018-01-30 18:19:04 -08002038 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08002039
Tao Bao40b18822018-01-30 18:19:04 -08002040 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08002041 payload_signer = PayloadSigner()
2042 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08002043
Tao Bao40b18822018-01-30 18:19:04 -08002044 # Write the payload into output zip.
2045 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002046
Tao Baof7140c02018-01-30 17:09:24 -08002047 # Generate and include the secondary payload that installs secondary images
2048 # (e.g. system_other.img).
2049 if OPTIONS.include_secondary:
2050 # We always include a full payload for the secondary slot, even when
2051 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08002052 secondary_target_file = GetTargetFilesZipForSecondaryImages(
2053 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08002054 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08002055 secondary_payload.Generate(secondary_target_file,
2056 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08002057 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08002058 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08002059
Tianjie Xucfa86222016-03-07 16:31:19 -08002060 # If dm-verity is supported for the device, copy contents of care_map
2061 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07002062 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08002063 if (target_info.get("verity") == "true" or
2064 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002065 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
2066 "META/" + x in target_zip.namelist()]
2067
2068 # Adds care_map if either the protobuf format or the plain text one exists.
2069 if care_map_list:
2070 care_map_name = care_map_list[0]
2071 care_map_data = target_zip.read("META/" + care_map_name)
2072 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08002073 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002074 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002075 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002076 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002077 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002078
Tao Baobcd1d162017-08-26 13:10:26 -07002079 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08002080 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07002081
Tao Bao21803d32017-04-19 10:16:09 -07002082 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002083
Tao Baofe5b69a2018-03-02 09:47:43 -08002084 # We haven't written the metadata entry yet, which will be handled in
2085 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002086 common.ZipClose(output_zip)
2087
Tao Bao85f16982018-03-08 16:28:33 -08002088 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2089 # all the info of the latter. However, system updaters and OTA servers need to
2090 # take time to switch to the new flag. We keep both of the flags for
2091 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002092 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002093 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002094 StreamingPropertyFiles(),
2095 )
2096 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002097
Tao Baoc098e9e2016-01-07 13:03:56 -08002098
Doug Zongkereef39442009-04-02 12:14:19 -07002099def main(argv):
2100
2101 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002102 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002103 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002104 elif o in ("-i", "--incremental_from"):
2105 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002106 elif o == "--full_radio":
2107 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002108 elif o == "--full_bootloader":
2109 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002110 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002111 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002112 elif o == "--downgrade":
2113 OPTIONS.downgrade = True
2114 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002115 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002116 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002117 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002118 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002119 elif o == "--oem_no_mount":
2120 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002121 elif o in ("-e", "--extra_script"):
2122 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002123 elif o in ("-t", "--worker_threads"):
2124 if a.isdigit():
2125 OPTIONS.worker_threads = int(a)
2126 else:
2127 raise ValueError("Cannot parse value %r for option %r - only "
2128 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002129 elif o in ("-2", "--two_step"):
2130 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002131 elif o == "--include_secondary":
2132 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002133 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002134 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002135 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002136 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002137 elif o == "--block":
2138 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002139 elif o in ("-b", "--binary"):
2140 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002141 elif o == "--stash_threshold":
2142 try:
2143 OPTIONS.stash_threshold = float(a)
2144 except ValueError:
2145 raise ValueError("Cannot parse value %r for option %r - expecting "
2146 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002147 elif o == "--log_diff":
2148 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002149 elif o == "--payload_signer":
2150 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002151 elif o == "--payload_signer_args":
2152 OPTIONS.payload_signer_args = shlex.split(a)
xunchang376cc7c2019-04-08 23:04:58 -07002153 elif o == "--payload_signer_key_size":
2154 OPTIONS.payload_signer_key_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002155 elif o == "--extracted_input_target_files":
2156 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002157 elif o == "--skip_postinstall":
2158 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002159 elif o == "--retrofit_dynamic_partitions":
2160 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002161 elif o == "--skip_compatibility_check":
2162 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002163 elif o == "--output_metadata_path":
2164 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002165 else:
2166 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002167 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002168
2169 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002170 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002171 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002172 "package_key=",
2173 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002174 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002175 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002176 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002177 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002178 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002179 "extra_script=",
2180 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002181 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002182 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002183 "no_signing",
2184 "block",
2185 "binary=",
2186 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002187 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002188 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002189 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002190 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002191 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002192 "payload_signer_args=",
xunchang376cc7c2019-04-08 23:04:58 -07002193 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002194 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002195 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002196 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002197 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002198 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002199 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002200
2201 if len(args) != 2:
2202 common.Usage(__doc__)
2203 sys.exit(1)
2204
Tao Bao32fcdab2018-10-12 10:30:39 -07002205 common.InitLogging()
2206
Tao Bao5d182562016-02-23 11:38:39 -08002207 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002208 # We should only allow downgrading incrementals (as opposed to full).
2209 # Otherwise the device may go back from arbitrary build with this full
2210 # OTA package.
2211 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002212 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002213
Tao Bao2db13852018-01-08 22:28:57 -08002214 # Load the build info dicts from the zip directly or the extracted input
2215 # directory. We don't need to unzip the entire target-files zips, because they
2216 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2217 # When loading the info dicts, we don't need to provide the second parameter
2218 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2219 # some properties with their actual paths, such as 'selinux_fc',
2220 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002221 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002222 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002223 else:
Tao Bao2db13852018-01-08 22:28:57 -08002224 with zipfile.ZipFile(args[0], 'r') as input_zip:
2225 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002226
Tao Bao32fcdab2018-10-12 10:30:39 -07002227 logger.info("--- target info ---")
2228 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002229
2230 # Load the source build dict if applicable.
2231 if OPTIONS.incremental_source is not None:
2232 OPTIONS.target_info_dict = OPTIONS.info_dict
2233 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2234 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2235
Tao Bao32fcdab2018-10-12 10:30:39 -07002236 logger.info("--- source info ---")
2237 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002238
2239 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002240 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2241
Yifan Hong50e79542018-11-08 17:44:12 -08002242 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002243 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002244 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002245 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2246 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002247 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2248 raise common.ExternalError(
2249 "Expect to generate incremental OTA for retrofitting dynamic "
2250 "partitions, but dynamic_partition_retrofit is not set in target "
2251 "build.")
2252 logger.info("Implicitly generating retrofit incremental OTA.")
2253 OPTIONS.retrofit_dynamic_partitions = True
2254
2255 # Skip postinstall for retrofitting dynamic partitions.
2256 if OPTIONS.retrofit_dynamic_partitions:
2257 OPTIONS.skip_postinstall = True
2258
Tao Baoc098e9e2016-01-07 13:03:56 -08002259 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2260
Christian Oderf63e2cd2017-05-01 22:30:15 +02002261 # Use the default key to sign the package if not specified with package_key.
2262 # package_keys are needed on ab_updates, so always define them if an
2263 # ab_update is getting created.
2264 if not OPTIONS.no_signing or ab_update:
2265 if OPTIONS.package_key is None:
2266 OPTIONS.package_key = OPTIONS.info_dict.get(
2267 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002268 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002269 # Get signing keys
2270 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2271
Tao Baoc098e9e2016-01-07 13:03:56 -08002272 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002273 WriteABOTAPackageWithBrilloScript(
2274 target_file=args[0],
2275 output_file=args[1],
2276 source_file=OPTIONS.incremental_source)
2277
Tao Bao32fcdab2018-10-12 10:30:39 -07002278 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002279 return
2280
Tao Bao2db13852018-01-08 22:28:57 -08002281 # Sanity check the loaded info dicts first.
2282 if OPTIONS.info_dict.get("no_recovery") == "true":
2283 raise common.ExternalError(
2284 "--- target build has specified no recovery ---")
2285
2286 # Non-A/B OTAs rely on /cache partition to store temporary files.
2287 cache_size = OPTIONS.info_dict.get("cache_size")
2288 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002289 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002290 OPTIONS.cache_size = cache_size
2291
Doug Zongker1c390a22009-05-14 19:06:36 -07002292 if OPTIONS.extra_script is not None:
Tao Bao59cf0c52019-06-25 10:04:24 -07002293 with open(OPTIONS.extra_script) as fp:
2294 OPTIONS.extra_script = fp.read()
Doug Zongker1c390a22009-05-14 19:06:36 -07002295
Dan Willemsencea5cd22017-03-21 14:44:27 -07002296 if OPTIONS.extracted_input is not None:
2297 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002298 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002299 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002300 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002301 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002302
Tao Bao2db13852018-01-08 22:28:57 -08002303 # If the caller explicitly specified the device-specific extensions path via
2304 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2305 # is present in the target target_files. Otherwise, take the path of the file
2306 # from 'tool_extensions' in the info dict and look for that in the local
2307 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002308 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002309 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2310 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002311 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002312 OPTIONS.device_specific = from_input
2313 else:
Tao Bao2db13852018-01-08 22:28:57 -08002314 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002315
Doug Zongker37974732010-09-16 17:44:38 -07002316 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002317 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002318
Tao Bao767e3ac2015-11-10 12:19:19 -08002319 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002320 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002321 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002322 WriteFullOTAPackage(
2323 input_zip,
2324 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002325
Tao Bao32b80dc2018-01-08 22:50:47 -08002326 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002327 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002328 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002329 OPTIONS.source_tmp = common.UnzipTemp(
2330 OPTIONS.incremental_source, UNZIP_PATTERN)
2331 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2332 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002333 WriteBlockIncrementalOTAPackage(
2334 input_zip,
2335 source_zip,
2336 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002337
2338 if OPTIONS.log_diff:
2339 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002340 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002341 target_files_diff.recursiveDiff(
2342 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002343
Tao Bao32fcdab2018-10-12 10:30:39 -07002344 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002345
2346
2347if __name__ == '__main__':
2348 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002349 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002350 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002351 except common.ExternalError:
2352 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002353 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002354 finally:
2355 common.Cleanup()