blob: ad40bd4ac41d2584410d9bec3a3fb097f0ac2306 [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
Dan Willemsen0ab1be62019-04-09 21:35:37 -070041 META/misc_info.txt, or "build/make/target/product/security/testkey" if that
Tao Bao30df8b42018-04-23 15:32:53 -070042 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):
321 if "vendor.build.prop" not in self.info_dict:
322 return None
323 vendor_build_prop = self.info_dict["vendor.build.prop"]
324 if "ro.vendor.build.fingerprint" in vendor_build_prop:
325 return vendor_build_prop["ro.vendor.build.fingerprint"]
326 if "ro.vendor.build.thumbprint" in vendor_build_prop:
327 return vendor_build_prop["ro.vendor.build.thumbprint"]
328 return None
329
330 @property
Tao Bao481bab82017-12-21 11:23:09 -0800331 def oem_props(self):
332 return self._oem_props
333
334 def __getitem__(self, key):
335 return self.info_dict[key]
336
Tao Bao667c7532018-07-06 10:13:59 -0700337 def __setitem__(self, key, value):
338 self.info_dict[key] = value
339
Tao Bao481bab82017-12-21 11:23:09 -0800340 def get(self, key, default=None):
341 return self.info_dict.get(key, default)
342
Tao Bao667c7532018-07-06 10:13:59 -0700343 def items(self):
344 return self.info_dict.items()
345
Tao Bao481bab82017-12-21 11:23:09 -0800346 def GetBuildProp(self, prop):
347 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800348 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
349 return self._ResolveRoProductBuildProp(prop)
350
Tao Bao481bab82017-12-21 11:23:09 -0800351 try:
352 return self.info_dict.get("build.prop", {})[prop]
353 except KeyError:
354 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
355
Steven Laver9e73e822019-01-29 20:20:08 -0800356 def _ResolveRoProductBuildProp(self, prop):
357 """Resolves the inquired ro.product.* build property"""
358 prop_val = self.info_dict.get("build.prop", {}).get(prop)
359 if prop_val:
360 return prop_val
361
362 source_order_val = self.info_dict.get("build.prop", {}).get(
363 "ro.product.property_source_order")
364 if source_order_val:
365 source_order = source_order_val.split(",")
366 else:
367 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
368
369 # Check that all sources in ro.product.property_source_order are valid
370 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
371 for x in source_order]):
372 raise common.ExternalError(
373 "Invalid ro.product.property_source_order '{}'".format(source_order))
374
375 for source in source_order:
376 source_prop = prop.replace("ro.product", "ro.product.{}".format(source),
377 1)
378 prop_val = self.info_dict.get("{}.build.prop".format(source), {}).get(
379 source_prop)
380 if prop_val:
381 return prop_val
382
383 raise common.ExternalError("couldn't resolve {}".format(prop))
384
Tao Bao481bab82017-12-21 11:23:09 -0800385 def GetVendorBuildProp(self, prop):
386 """Returns the inquired vendor build property."""
387 try:
388 return self.info_dict.get("vendor.build.prop", {})[prop]
389 except KeyError:
390 raise common.ExternalError(
391 "couldn't find %s in vendor.build.prop" % (prop,))
392
393 def GetOemProperty(self, key):
394 if self.oem_props is not None and key in self.oem_props:
395 return self.oem_dicts[0][key]
396 return self.GetBuildProp(key)
397
398 def CalculateFingerprint(self):
399 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800400 try:
401 return self.GetBuildProp("ro.build.fingerprint")
402 except common.ExternalError:
403 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
404 self.GetBuildProp("ro.product.brand"),
405 self.GetBuildProp("ro.product.name"),
406 self.GetBuildProp("ro.product.device"),
407 self.GetBuildProp("ro.build.version.release"),
408 self.GetBuildProp("ro.build.id"),
409 self.GetBuildProp("ro.build.version.incremental"),
410 self.GetBuildProp("ro.build.type"),
411 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800412 return "%s/%s/%s:%s" % (
413 self.GetOemProperty("ro.product.brand"),
414 self.GetOemProperty("ro.product.name"),
415 self.GetOemProperty("ro.product.device"),
416 self.GetBuildProp("ro.build.thumbprint"))
417
418 def WriteMountOemScript(self, script):
419 assert self.oem_props is not None
420 recovery_mount_options = self.info_dict.get("recovery_mount_options")
421 script.Mount("/oem", recovery_mount_options)
422
423 def WriteDeviceAssertions(self, script, oem_no_mount):
424 # Read the property directly if not using OEM properties.
425 if not self.oem_props:
426 script.AssertDevice(self.device)
427 return
428
429 # Otherwise assert OEM properties.
430 if not self.oem_dicts:
431 raise common.ExternalError(
432 "No OEM file provided to answer expected assertions")
433
434 for prop in self.oem_props.split():
435 values = []
436 for oem_dict in self.oem_dicts:
437 if prop in oem_dict:
438 values.append(oem_dict[prop])
439 if not values:
440 raise common.ExternalError(
441 "The OEM file is missing the property %s" % (prop,))
442 script.AssertOemProperty(prop, values, oem_no_mount)
443
444
Tao Baofabe0832018-01-17 15:52:28 -0800445class PayloadSigner(object):
446 """A class that wraps the payload signing works.
447
448 When generating a Payload, hashes of the payload and metadata files will be
449 signed with the device key, either by calling an external payload signer or
450 by calling openssl with the package key. This class provides a unified
451 interface, so that callers can just call PayloadSigner.Sign().
452
453 If an external payload signer has been specified (OPTIONS.payload_signer), it
454 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
455 that the signing key should be provided as part of the payload_signer_args.
456 Otherwise without an external signer, it uses the package key
457 (OPTIONS.package_key) and calls openssl for the signing works.
458 """
459
460 def __init__(self):
461 if OPTIONS.payload_signer is None:
462 # Prepare the payload signing key.
463 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
464 pw = OPTIONS.key_passwords[OPTIONS.package_key]
465
466 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
467 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
468 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
469 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700470 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800471
472 self.signer = "openssl"
473 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
474 "-pkeyopt", "digest:sha256"]
xunchang376cc7c2019-04-08 23:04:58 -0700475 self.key_size = self._GetKeySizeInBytes(signing_key)
Tao Baofabe0832018-01-17 15:52:28 -0800476 else:
477 self.signer = OPTIONS.payload_signer
478 self.signer_args = OPTIONS.payload_signer_args
xunchang376cc7c2019-04-08 23:04:58 -0700479 if OPTIONS.payload_signer_key_size:
480 self.key_size = int(OPTIONS.payload_signer_key_size)
481 assert self.key_size == 256 or self.key_size == 512, \
482 "Unsupported key size {}".format(OPTIONS.payload_signer_key_size)
483 else:
484 self.key_size = 256
485
486 @staticmethod
487 def _GetKeySizeInBytes(signing_key):
488 modulus_file = common.MakeTempFile(prefix="modulus-")
489 cmd = ["openssl", "rsa", "-inform", "PEM", "-in", signing_key, "-modulus",
490 "-noout", "-out", modulus_file]
491 common.RunAndCheckOutput(cmd, verbose=False)
492
493 with open(modulus_file) as f:
494 modulus_string = f.read()
495 # The modulus string has the format "Modulus=$data", where $data is the
496 # concatenation of hex dump of the modulus.
497 MODULUS_PREFIX = "Modulus="
498 assert modulus_string.startswith(MODULUS_PREFIX)
499 modulus_string = modulus_string[len(MODULUS_PREFIX):]
500 key_size = len(modulus_string) / 2
501 assert key_size == 256 or key_size == 512, \
502 "Unsupported key size {}".format(key_size)
503 return key_size
Tao Baofabe0832018-01-17 15:52:28 -0800504
505 def Sign(self, in_file):
506 """Signs the given input file. Returns the output filename."""
507 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
508 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700509 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800510 return out_file
511
512
Tao Bao40b18822018-01-30 18:19:04 -0800513class Payload(object):
514 """Manages the creation and the signing of an A/B OTA Payload."""
515
516 PAYLOAD_BIN = 'payload.bin'
517 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800518 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
519 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800520
Tao Bao667ff572018-02-10 00:02:40 -0800521 def __init__(self, secondary=False):
522 """Initializes a Payload instance.
523
524 Args:
525 secondary: Whether it's generating a secondary payload (default: False).
526 """
Tao Bao40b18822018-01-30 18:19:04 -0800527 self.payload_file = None
528 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800529 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800530
531 def Generate(self, target_file, source_file=None, additional_args=None):
532 """Generates a payload from the given target-files zip(s).
533
534 Args:
535 target_file: The filename of the target build target-files zip.
536 source_file: The filename of the source build target-files zip; or None if
537 generating a full OTA.
538 additional_args: A list of additional args that should be passed to
539 brillo_update_payload script; or None.
540 """
541 if additional_args is None:
542 additional_args = []
543
544 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
545 cmd = ["brillo_update_payload", "generate",
546 "--payload", payload_file,
547 "--target_image", target_file]
548 if source_file is not None:
549 cmd.extend(["--source_image", source_file])
550 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700551 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800552
553 self.payload_file = payload_file
554 self.payload_properties = None
555
556 def Sign(self, payload_signer):
557 """Generates and signs the hashes of the payload and metadata.
558
559 Args:
560 payload_signer: A PayloadSigner() instance that serves the signing work.
561
562 Raises:
563 AssertionError: On any failure when calling brillo_update_payload script.
564 """
565 assert isinstance(payload_signer, PayloadSigner)
566
567 # 1. Generate hashes of the payload and metadata files.
568 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
569 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
570 cmd = ["brillo_update_payload", "hash",
571 "--unsigned_payload", self.payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700572 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800573 "--metadata_hash_file", metadata_sig_file,
574 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700575 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800576
577 # 2. Sign the hashes.
578 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
579 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
580
581 # 3. Insert the signatures back into the payload file.
582 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
583 suffix=".bin")
584 cmd = ["brillo_update_payload", "sign",
585 "--unsigned_payload", self.payload_file,
586 "--payload", signed_payload_file,
xunchang376cc7c2019-04-08 23:04:58 -0700587 "--signature_size", str(payload_signer.key_size),
Tao Bao40b18822018-01-30 18:19:04 -0800588 "--metadata_signature_file", signed_metadata_sig_file,
589 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700590 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800591
592 # 4. Dump the signed payload properties.
593 properties_file = common.MakeTempFile(prefix="payload-properties-",
594 suffix=".txt")
595 cmd = ["brillo_update_payload", "properties",
596 "--payload", signed_payload_file,
597 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700598 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800599
Tao Bao667ff572018-02-10 00:02:40 -0800600 if self.secondary:
601 with open(properties_file, "a") as f:
602 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
603
Tao Bao40b18822018-01-30 18:19:04 -0800604 if OPTIONS.wipe_user_data:
605 with open(properties_file, "a") as f:
606 f.write("POWERWASH=1\n")
607
608 self.payload_file = signed_payload_file
609 self.payload_properties = properties_file
610
Tao Bao667ff572018-02-10 00:02:40 -0800611 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800612 """Writes the payload to the given zip.
613
614 Args:
615 output_zip: The output ZipFile instance.
616 """
617 assert self.payload_file is not None
618 assert self.payload_properties is not None
619
Tao Bao667ff572018-02-10 00:02:40 -0800620 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800621 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
622 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
623 else:
624 payload_arcname = Payload.PAYLOAD_BIN
625 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
626
Tao Bao40b18822018-01-30 18:19:04 -0800627 # Add the signed payload file and properties into the zip. In order to
628 # support streaming, we pack them as ZIP_STORED. So these entries can be
629 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800630 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800631 compress_type=zipfile.ZIP_STORED)
632 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800633 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800634 compress_type=zipfile.ZIP_STORED)
635
636
Doug Zongkereef39442009-04-02 12:14:19 -0700637def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200638 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700639
Doug Zongker951495f2009-08-14 12:44:19 -0700640 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
641 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700642
643
Tao Bao481bab82017-12-21 11:23:09 -0800644def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800645 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800646 if not oem_source:
647 return None
648
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800649 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800650 for oem_file in oem_source:
651 with open(oem_file) as fp:
652 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800653 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700654
Doug Zongkereef39442009-04-02 12:14:19 -0700655
Tao Baod42e97e2016-11-30 12:11:57 -0800656def _WriteRecoveryImageToBoot(script, output_zip):
657 """Find and write recovery image to /boot in two-step OTA.
658
659 In two-step OTAs, we write recovery image to /boot as the first step so that
660 we can reboot to there and install a new recovery image to /recovery.
661 A special "recovery-two-step.img" will be preferred, which encodes the correct
662 path of "/boot". Otherwise the device may show "device is corrupt" message
663 when booting into /boot.
664
665 Fall back to using the regular recovery.img if the two-step recovery image
666 doesn't exist. Note that rebuilding the special image at this point may be
667 infeasible, because we don't have the desired boot signer and keys when
668 calling ota_from_target_files.py.
669 """
670
671 recovery_two_step_img_name = "recovery-two-step.img"
672 recovery_two_step_img_path = os.path.join(
673 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
674 if os.path.exists(recovery_two_step_img_path):
675 recovery_two_step_img = common.GetBootableImage(
676 recovery_two_step_img_name, recovery_two_step_img_name,
677 OPTIONS.input_tmp, "RECOVERY")
678 common.ZipWriteStr(
679 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700680 logger.info(
681 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800682 script.WriteRawImage("/boot", recovery_two_step_img_name)
683 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700684 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800685 # The "recovery.img" entry has been written into package earlier.
686 script.WriteRawImage("/boot", "recovery.img")
687
688
Doug Zongkerc9253822014-02-04 12:17:58 -0800689def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700690 namelist = [name for name in target_files_zip.namelist()]
691 return ("SYSTEM/recovery-from-boot.p" in namelist or
692 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700693
Tao Bao457cbf62017-03-06 09:56:01 -0800694
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700695def HasVendorPartition(target_files_zip):
696 try:
697 target_files_zip.getinfo("VENDOR/")
698 return True
699 except KeyError:
700 return False
701
Tao Bao457cbf62017-03-06 09:56:01 -0800702
Tao Bao481bab82017-12-21 11:23:09 -0800703def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700704 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800705 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700706
707
Tao Bao481bab82017-12-21 11:23:09 -0800708def WriteFingerprintAssertion(script, target_info, source_info):
709 source_oem_props = source_info.oem_props
710 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700711
Tao Bao481bab82017-12-21 11:23:09 -0800712 if source_oem_props is None and target_oem_props is None:
713 script.AssertSomeFingerprint(
714 source_info.fingerprint, target_info.fingerprint)
715 elif source_oem_props is not None and target_oem_props is not None:
716 script.AssertSomeThumbprint(
717 target_info.GetBuildProp("ro.build.thumbprint"),
718 source_info.GetBuildProp("ro.build.thumbprint"))
719 elif source_oem_props is None and target_oem_props is not None:
720 script.AssertFingerprintOrThumbprint(
721 source_info.fingerprint,
722 target_info.GetBuildProp("ro.build.thumbprint"))
723 else:
724 script.AssertFingerprintOrThumbprint(
725 target_info.fingerprint,
726 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700727
Doug Zongkerfc44a512014-08-26 13:10:25 -0700728
Tao Bao481bab82017-12-21 11:23:09 -0800729def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
730 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700731 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700732
733 Metadata used for on-device compatibility verification is retrieved from
734 target_zip then added to compatibility.zip which is added to the output_zip
735 archive.
736
Tao Baobcd1d162017-08-26 13:10:26 -0700737 Compatibility archive should only be included for devices that have enabled
738 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700739
740 Args:
741 target_zip: Zip file containing the source files to be included for OTA.
742 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800743 target_info: The BuildInfo instance that holds the target build info.
744 source_info: The BuildInfo instance that holds the source build info, if
745 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700746 """
747
Tao Baobcd1d162017-08-26 13:10:26 -0700748 def AddCompatibilityArchive(system_updated, vendor_updated):
749 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700750
Tao Baobcd1d162017-08-26 13:10:26 -0700751 Args:
752 system_updated: If True, the system image will be updated and therefore
753 its metadata should be included.
754 vendor_updated: If True, the vendor image will be updated and therefore
755 its metadata should be included.
756 """
757 # Determine what metadata we need. Files are names relative to META/.
758 compatibility_files = []
759 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
760 system_metadata = ("system_manifest.xml", "system_matrix.xml")
761 if vendor_updated:
762 compatibility_files += vendor_metadata
763 if system_updated:
764 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700765
Tao Baobcd1d162017-08-26 13:10:26 -0700766 # Create new archive.
767 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800768 compatibility_archive_zip = zipfile.ZipFile(
769 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700770
Tao Baobcd1d162017-08-26 13:10:26 -0700771 # Add metadata.
772 for file_name in compatibility_files:
773 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700774
Tao Baobcd1d162017-08-26 13:10:26 -0700775 if target_file_name in target_zip.namelist():
776 data = target_zip.read(target_file_name)
777 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700778
Tao Baobcd1d162017-08-26 13:10:26 -0700779 # Ensure files are written before we copy into output_zip.
780 compatibility_archive_zip.close()
781
782 # Only add the archive if we have any compatibility info.
783 if compatibility_archive_zip.namelist():
784 common.ZipWrite(output_zip, compatibility_archive.name,
785 arcname="compatibility.zip",
786 compress_type=zipfile.ZIP_STORED)
787
788 # Will only proceed if the target has enabled the Treble support (as well as
789 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800790 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700791 return
792
xunchangabfa2652019-02-19 16:27:10 -0800793 # Skip adding the compatibility package as a workaround for b/114240221. The
794 # compatibility will always fail on devices without qualified kernels.
795 if OPTIONS.skip_compatibility_check:
796 return
797
Tao Baobcd1d162017-08-26 13:10:26 -0700798 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800799 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700800 AddCompatibilityArchive(True, True)
801 return
802
Tao Bao481bab82017-12-21 11:23:09 -0800803 source_fp = source_info.fingerprint
804 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700805 system_updated = source_fp != target_fp
806
Tao Baoea6cbd02018-09-05 13:06:37 -0700807 source_fp_vendor = source_info.vendor_fingerprint
808 target_fp_vendor = target_info.vendor_fingerprint
809 # vendor build fingerprints could be possibly blacklisted at build time. For
810 # such a case, we consider the vendor images being changed.
811 if source_fp_vendor is None or target_fp_vendor is None:
812 vendor_updated = True
813 else:
814 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700815
816 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700817
818
Tao Bao491d7e22018-02-21 13:17:22 -0800819def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800820 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700821
Tao Bao481bab82017-12-21 11:23:09 -0800822 # We don't know what version it will be installed on top of. We expect the API
823 # just won't change very often. Similarly for fstab, it might have changed in
824 # the target build.
825 target_api_version = target_info["recovery_api_version"]
826 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700827
Tao Bao481bab82017-12-21 11:23:09 -0800828 if target_info.oem_props and not OPTIONS.oem_no_mount:
829 target_info.WriteMountOemScript(script)
830
Tao Baodf3a48b2018-01-10 16:30:43 -0800831 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700832
Tao Bao491d7e22018-02-21 13:17:22 -0800833 if not OPTIONS.no_signing:
834 staging_file = common.MakeTempFile(suffix='.zip')
835 else:
836 staging_file = output_file
837
838 output_zip = zipfile.ZipFile(
839 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
840
Doug Zongker05d3dea2009-06-22 11:32:31 -0700841 device_specific = common.DeviceSpecificParams(
842 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800843 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700844 output_zip=output_zip,
845 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700846 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700847 metadata=metadata,
848 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700849
Tao Bao457cbf62017-03-06 09:56:01 -0800850 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800851
Tao Bao481bab82017-12-21 11:23:09 -0800852 # Assertions (e.g. downgrade check, device properties check).
853 ts = target_info.GetBuildProp("ro.build.date.utc")
854 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700855 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700856
Tao Bao481bab82017-12-21 11:23:09 -0800857 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700858 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800859
860 # Two-step package strategy (in chronological order, which is *not*
861 # the order in which the generated script has things):
862 #
863 # if stage is not "2/3" or "3/3":
864 # write recovery image to boot partition
865 # set stage to "2/3"
866 # reboot to boot partition and restart recovery
867 # else if stage is "2/3":
868 # write recovery image to recovery partition
869 # set stage to "3/3"
870 # reboot to recovery partition and restart recovery
871 # else:
872 # (stage must be "3/3")
873 # set stage to ""
874 # do normal full package installation:
875 # wipe and install system, boot image, etc.
876 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700877 # complete script normally
878 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800879
880 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
881 OPTIONS.input_tmp, "RECOVERY")
882 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800883 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800884 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800885 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800886 assert fs.fs_type.upper() == "EMMC", \
887 "two-step packages only supported on devices with EMMC /misc partitions"
888 bcb_dev = {"bcb_dev": fs.device}
889 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
890 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700891if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800892""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800893
894 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
895 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800896 script.WriteRawImage("/recovery", "recovery.img")
897 script.AppendExtra("""
898set_stage("%(bcb_dev)s", "3/3");
899reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700900else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800901""" % bcb_dev)
902
Tao Baod42e97e2016-11-30 12:11:57 -0800903 # Stage 3/3: Make changes.
904 script.Comment("Stage 3/3")
905
Tao Bao6c55a8a2015-04-08 15:30:27 -0700906 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800907 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700908
Doug Zongkere5ff5902012-01-17 10:55:37 -0800909 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700910
Doug Zongker01ce19c2014-02-04 13:48:15 -0800911 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700912
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700913 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800914 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700915 if HasVendorPartition(input_zip):
916 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700917
Doug Zongker4b9596f2014-06-09 14:15:45 -0700918 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800919
Tao Baoe709b092018-02-07 12:40:00 -0800920 # See the notes in WriteBlockIncrementalOTAPackage().
921 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
922
Yifan Hong10c530d2018-12-27 17:34:18 -0800923 def GetBlockDifference(partition):
924 # Full OTA is done as an "incremental" against an empty source image. This
925 # has the effect of writing new data from the package to the entire
926 # partition, but lets us reuse the updater code that writes incrementals to
927 # do it.
928 tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
929 allow_shared_blocks)
930 tgt.ResetFileMap()
931 diff = common.BlockDifference(partition, tgt, src=None)
932 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700933
Yifan Hong10c530d2018-12-27 17:34:18 -0800934 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
935 if device_specific_diffs:
936 assert all(isinstance(diff, common.BlockDifference)
937 for diff in device_specific_diffs), \
938 "FullOTA_GetBlockDifferences is not returning a list of " \
939 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800940
Yifan Hong10c530d2018-12-27 17:34:18 -0800941 progress_dict = dict()
942 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700943 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800944 block_diffs.append(GetBlockDifference("vendor"))
945 progress_dict["vendor"] = 0.1
946 if device_specific_diffs:
947 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700948
Yifan Hong10c530d2018-12-27 17:34:18 -0800949 if target_info.get('use_dynamic_partitions') == "true":
950 # Use empty source_info_dict to indicate that all partitions / groups must
951 # be re-added.
952 dynamic_partitions_diff = common.DynamicPartitionsDifference(
953 info_dict=OPTIONS.info_dict,
954 block_diffs=block_diffs,
955 progress_dict=progress_dict)
956 dynamic_partitions_diff.WriteScript(script, output_zip,
957 write_verify_script=OPTIONS.verify)
958 else:
959 for block_diff in block_diffs:
960 block_diff.WriteScript(script, output_zip,
961 progress=progress_dict.get(block_diff.partition),
962 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700963
Tao Bao481bab82017-12-21 11:23:09 -0800964 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700965
Yifan Hong10c530d2018-12-27 17:34:18 -0800966 boot_img = common.GetBootableImage(
967 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800968 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700969 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700970
Doug Zongker01ce19c2014-02-04 13:48:15 -0800971 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700972 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700973
Doug Zongker01ce19c2014-02-04 13:48:15 -0800974 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700975 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700976
Doug Zongker1c390a22009-05-14 19:06:36 -0700977 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700978 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700979
Doug Zongker14833602010-02-02 13:12:04 -0800980 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800981
Doug Zongker922206e2014-03-04 13:16:24 -0800982 if OPTIONS.wipe_user_data:
983 script.ShowProgress(0.1, 10)
984 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700985
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800986 if OPTIONS.two_step:
987 script.AppendExtra("""
988set_stage("%(bcb_dev)s", "");
989""" % bcb_dev)
990 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800991
992 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
993 script.Comment("Stage 1/3")
994 _WriteRecoveryImageToBoot(script, output_zip)
995
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800996 script.AppendExtra("""
997set_stage("%(bcb_dev)s", "2/3");
998reboot_now("%(bcb_dev)s", "");
999endif;
1000endif;
1001""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -08001002
Tao Bao5d182562016-02-23 11:38:39 -08001003 script.SetProgress(1)
1004 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001005 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001006
1007 # We haven't written the metadata entry, which will be done in
1008 # FinalizeMetadata.
1009 common.ZipClose(output_zip)
1010
1011 needed_property_files = (
1012 NonAbOtaPropertyFiles(),
1013 )
1014 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -07001015
Doug Zongkerfc44a512014-08-26 13:10:25 -07001016
xunchang1cfe2512019-02-19 14:14:48 -08001017def WriteMetadata(metadata, output):
1018 """Writes the metadata to the zip archive or a file.
1019
1020 Args:
1021 metadata: The metadata dict for the package.
1022 output: A ZipFile object or a string of the output file path.
1023 """
1024
Tao Bao2dd1c482017-02-03 16:49:39 -08001025 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
xunchang1cfe2512019-02-19 14:14:48 -08001026 if isinstance(output, zipfile.ZipFile):
1027 common.ZipWriteStr(output, METADATA_NAME, value,
1028 compress_type=zipfile.ZIP_STORED)
1029 return
1030
1031 with open(output, 'w') as f:
1032 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001033
Doug Zongkerfc44a512014-08-26 13:10:25 -07001034
Tao Bao481bab82017-12-21 11:23:09 -08001035def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001036 # Only incremental OTAs are allowed to reach here.
1037 assert OPTIONS.incremental_source is not None
1038
Tao Bao481bab82017-12-21 11:23:09 -08001039 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1040 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -08001041 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1042
1043 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001044 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001045 raise RuntimeError(
1046 "--downgrade or --override_timestamp specified but no downgrade "
1047 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001048 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001049 else:
1050 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001051 raise RuntimeError(
1052 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1053 "Need to specify --override_timestamp OR --downgrade to allow "
1054 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001055
1056
Tao Baodf3a48b2018-01-10 16:30:43 -08001057def GetPackageMetadata(target_info, source_info=None):
1058 """Generates and returns the metadata dict.
1059
1060 It generates a dict() that contains the info to be written into an OTA
1061 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001062 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001063
1064 Args:
1065 target_info: The BuildInfo instance that holds the target build info.
1066 source_info: The BuildInfo instance that holds the source build info, or
1067 None if generating full OTA.
1068
1069 Returns:
1070 A dict to be written into package metadata entry.
1071 """
1072 assert isinstance(target_info, BuildInfo)
1073 assert source_info is None or isinstance(source_info, BuildInfo)
1074
1075 metadata = {
1076 'post-build' : target_info.fingerprint,
1077 'post-build-incremental' : target_info.GetBuildProp(
1078 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001079 'post-sdk-level' : target_info.GetBuildProp(
1080 'ro.build.version.sdk'),
1081 'post-security-patch-level' : target_info.GetBuildProp(
1082 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001083 }
1084
1085 if target_info.is_ab:
1086 metadata['ota-type'] = 'AB'
1087 metadata['ota-required-cache'] = '0'
1088 else:
1089 metadata['ota-type'] = 'BLOCK'
1090
1091 if OPTIONS.wipe_user_data:
1092 metadata['ota-wipe'] = 'yes'
1093
Tao Bao393eeb42019-03-06 16:00:38 -08001094 if OPTIONS.retrofit_dynamic_partitions:
1095 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1096
Tao Baodf3a48b2018-01-10 16:30:43 -08001097 is_incremental = source_info is not None
1098 if is_incremental:
1099 metadata['pre-build'] = source_info.fingerprint
1100 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1101 'ro.build.version.incremental')
1102 metadata['pre-device'] = source_info.device
1103 else:
1104 metadata['pre-device'] = target_info.device
1105
Tao Baofaa8e0b2018-04-12 14:31:43 -07001106 # Use the actual post-timestamp, even for a downgrade case.
1107 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1108
1109 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001110 if is_incremental:
1111 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001112
1113 return metadata
1114
1115
Tao Baod3fc38a2018-03-08 16:09:01 -08001116class PropertyFiles(object):
1117 """A class that computes the property-files string for an OTA package.
1118
1119 A property-files string is a comma-separated string that contains the
1120 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1121 can be fetched directly with the package URL along with the offset/size info.
1122 These strings can be used for streaming A/B OTAs, or allowing an updater to
1123 download package metadata entry directly, without paying the cost of
1124 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001125
Tao Baocc8e2662018-03-01 19:30:00 -08001126 Computing the final property-files string requires two passes. Because doing
1127 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1128 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1129 values.
1130
1131 This class provides functions to be called for each pass. The general flow is
1132 as follows.
1133
Tao Baod3fc38a2018-03-08 16:09:01 -08001134 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001135 # The first pass, which writes placeholders before doing initial signing.
1136 property_files.Compute()
1137 SignOutput()
1138
1139 # The second pass, by replacing the placeholders with actual data.
1140 property_files.Finalize()
1141 SignOutput()
1142
1143 And the caller can additionally verify the final result.
1144
1145 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001146 """
1147
Tao Baocc8e2662018-03-01 19:30:00 -08001148 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001149 self.name = None
1150 self.required = ()
1151 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001152
Tao Baocc8e2662018-03-01 19:30:00 -08001153 def Compute(self, input_zip):
1154 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001155
Tao Baocc8e2662018-03-01 19:30:00 -08001156 We reserve extra space for the offset and size of the metadata entry itself,
1157 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001158
Tao Baocc8e2662018-03-01 19:30:00 -08001159 Args:
1160 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001161
Tao Baocc8e2662018-03-01 19:30:00 -08001162 Returns:
1163 A string with placeholders for the metadata offset/size info, e.g.
1164 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1165 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001166 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001167
Tao Baod2ce2ed2018-03-16 12:59:42 -07001168 class InsufficientSpaceException(Exception):
1169 pass
1170
Tao Baocc8e2662018-03-01 19:30:00 -08001171 def Finalize(self, input_zip, reserved_length):
1172 """Finalizes a property-files string with actual METADATA offset/size info.
1173
1174 The input ZIP file has been signed, with the ZIP entries in the desired
1175 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1176 the ZIP entry offsets and construct the property-files string with actual
1177 data. Note that during this process, we must pad the property-files string
1178 to the reserved length, so that the METADATA entry size remains the same.
1179 Otherwise the entries' offsets and sizes may change again.
1180
1181 Args:
1182 input_zip: The input ZIP file.
1183 reserved_length: The reserved length of the property-files string during
1184 the call to Compute(). The final string must be no more than this
1185 size.
1186
1187 Returns:
1188 A property-files string including the metadata offset/size info, e.g.
1189 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1190
1191 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001192 InsufficientSpaceException: If the reserved length is insufficient to hold
1193 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001194 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001195 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001196 if len(result) > reserved_length:
1197 raise self.InsufficientSpaceException(
1198 'Insufficient reserved space: reserved={}, actual={}'.format(
1199 reserved_length, len(result)))
1200
Tao Baocc8e2662018-03-01 19:30:00 -08001201 result += ' ' * (reserved_length - len(result))
1202 return result
1203
1204 def Verify(self, input_zip, expected):
1205 """Verifies the input ZIP file contains the expected property-files string.
1206
1207 Args:
1208 input_zip: The input ZIP file.
1209 expected: The property-files string that's computed from Finalize().
1210
1211 Raises:
1212 AssertionError: On finding a mismatch.
1213 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001214 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001215 assert actual == expected, \
1216 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1217
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001218 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1219 """
1220 Constructs the property-files string per request.
1221
1222 Args:
1223 zip_file: The input ZIP file.
1224 reserved_length: The reserved length of the property-files string.
1225
1226 Returns:
1227 A property-files string including the metadata offset/size info, e.g.
1228 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1229 """
Tao Baocc8e2662018-03-01 19:30:00 -08001230
1231 def ComputeEntryOffsetSize(name):
1232 """Computes the zip entry offset and size."""
1233 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001234 offset = info.header_offset
1235 offset += zipfile.sizeFileHeader
1236 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001237 size = info.file_size
1238 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1239
1240 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001241 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001242 for entry in self.required:
1243 tokens.append(ComputeEntryOffsetSize(entry))
1244 for entry in self.optional:
1245 if entry in zip_file.namelist():
1246 tokens.append(ComputeEntryOffsetSize(entry))
1247
1248 # 'META-INF/com/android/metadata' is required. We don't know its actual
1249 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001250 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1251 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1252 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1253 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001254 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001255 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001256 else:
1257 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1258
1259 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001260
Tao Bao85f16982018-03-08 16:28:33 -08001261 def _GetPrecomputed(self, input_zip):
1262 """Computes the additional tokens to be included into the property-files.
1263
1264 This applies to tokens without actual ZIP entries, such as
1265 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1266 that they can download the payload metadata directly with the info.
1267
1268 Args:
1269 input_zip: The input zip file.
1270
1271 Returns:
1272 A list of strings (tokens) to be added to the property-files string.
1273 """
1274 # pylint: disable=no-self-use
1275 # pylint: disable=unused-argument
1276 return []
1277
Tao Baofe5b69a2018-03-02 09:47:43 -08001278
Tao Baod3fc38a2018-03-08 16:09:01 -08001279class StreamingPropertyFiles(PropertyFiles):
1280 """A subclass for computing the property-files for streaming A/B OTAs."""
1281
1282 def __init__(self):
1283 super(StreamingPropertyFiles, self).__init__()
1284 self.name = 'ota-streaming-property-files'
1285 self.required = (
1286 # payload.bin and payload_properties.txt must exist.
1287 'payload.bin',
1288 'payload_properties.txt',
1289 )
1290 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001291 # care_map is available only if dm-verity is enabled.
1292 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001293 'care_map.txt',
1294 # compatibility.zip is available only if target supports Treble.
1295 'compatibility.zip',
1296 )
1297
1298
Tao Bao85f16982018-03-08 16:28:33 -08001299class AbOtaPropertyFiles(StreamingPropertyFiles):
1300 """The property-files for A/B OTA that includes payload_metadata.bin info.
1301
1302 Since P, we expose one more token (aka property-file), in addition to the ones
1303 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1304 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1305 doesn't exist as a separate ZIP entry, but can be used to verify if the
1306 payload can be applied on the given device.
1307
1308 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1309 and the newly added 'ota-property-files' in P. The new token will only be
1310 available in 'ota-property-files'.
1311 """
1312
1313 def __init__(self):
1314 super(AbOtaPropertyFiles, self).__init__()
1315 self.name = 'ota-property-files'
1316
1317 def _GetPrecomputed(self, input_zip):
1318 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1319 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1320
1321 @staticmethod
1322 def _GetPayloadMetadataOffsetAndSize(input_zip):
1323 """Computes the offset and size of the payload metadata for a given package.
1324
1325 (From system/update_engine/update_metadata.proto)
1326 A delta update file contains all the deltas needed to update a system from
1327 one specific version to another specific version. The update format is
1328 represented by this struct pseudocode:
1329
1330 struct delta_update_file {
1331 char magic[4] = "CrAU";
1332 uint64 file_format_version;
1333 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1334
1335 // Only present if format_version > 1:
1336 uint32 metadata_signature_size;
1337
1338 // The Bzip2 compressed DeltaArchiveManifest
1339 char manifest[metadata_signature_size];
1340
1341 // The signature of the metadata (from the beginning of the payload up to
1342 // this location, not including the signature itself). This is a
1343 // serialized Signatures message.
1344 char medatada_signature_message[metadata_signature_size];
1345
1346 // Data blobs for files, no specific format. The specific offset
1347 // and length of each data blob is recorded in the DeltaArchiveManifest.
1348 struct {
1349 char data[];
1350 } blobs[];
1351
1352 // These two are not signed:
1353 uint64 payload_signatures_message_size;
1354 char payload_signatures_message[];
1355 };
1356
1357 'payload-metadata.bin' contains all the bytes from the beginning of the
1358 payload, till the end of 'medatada_signature_message'.
1359 """
1360 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001361 payload_offset = payload_info.header_offset
1362 payload_offset += zipfile.sizeFileHeader
1363 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001364 payload_size = payload_info.file_size
1365
1366 with input_zip.open('payload.bin', 'r') as payload_fp:
1367 header_bin = payload_fp.read(24)
1368
1369 # network byte order (big-endian)
1370 header = struct.unpack("!IQQL", header_bin)
1371
1372 # 'CrAU'
1373 magic = header[0]
1374 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1375
1376 manifest_size = header[2]
1377 metadata_signature_size = header[3]
1378 metadata_total = 24 + manifest_size + metadata_signature_size
1379 assert metadata_total < payload_size
1380
1381 return (payload_offset, metadata_total)
1382
1383
Tao Bao491d7e22018-02-21 13:17:22 -08001384class NonAbOtaPropertyFiles(PropertyFiles):
1385 """The property-files for non-A/B OTA.
1386
1387 For non-A/B OTA, the property-files string contains the info for METADATA
1388 entry, with which a system updater can be fetched the package metadata prior
1389 to downloading the entire package.
1390 """
1391
1392 def __init__(self):
1393 super(NonAbOtaPropertyFiles, self).__init__()
1394 self.name = 'ota-property-files'
1395
1396
Tao Baod3fc38a2018-03-08 16:09:01 -08001397def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001398 """Finalizes the metadata and signs an A/B OTA package.
1399
1400 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1401 that contains the offsets and sizes for the ZIP entries. An example
1402 property-files string is as follows.
1403
1404 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1405
1406 OTA server can pass down this string, in addition to the package URL, to the
1407 system update client. System update client can then fetch individual ZIP
1408 entries (ZIP_STORED) directly at the given offset of the URL.
1409
1410 Args:
1411 metadata: The metadata dict for the package.
1412 input_file: The input ZIP filename that doesn't contain the package METADATA
1413 entry yet.
1414 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001415 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001416 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001417
Tao Baod2ce2ed2018-03-16 12:59:42 -07001418 def ComputeAllPropertyFiles(input_file, needed_property_files):
1419 # Write the current metadata entry with placeholders.
1420 with zipfile.ZipFile(input_file) as input_zip:
1421 for property_files in needed_property_files:
1422 metadata[property_files.name] = property_files.Compute(input_zip)
1423 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001424
Tao Baod2ce2ed2018-03-16 12:59:42 -07001425 if METADATA_NAME in namelist:
1426 common.ZipDelete(input_file, METADATA_NAME)
1427 output_zip = zipfile.ZipFile(input_file, 'a')
1428 WriteMetadata(metadata, output_zip)
1429 common.ZipClose(output_zip)
1430
1431 if OPTIONS.no_signing:
1432 return input_file
1433
Tao Bao491d7e22018-02-21 13:17:22 -08001434 prelim_signing = common.MakeTempFile(suffix='.zip')
1435 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001436 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001437
Tao Baod2ce2ed2018-03-16 12:59:42 -07001438 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1439 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1440 for property_files in needed_property_files:
1441 metadata[property_files.name] = property_files.Finalize(
1442 prelim_signing_zip, len(metadata[property_files.name]))
1443
1444 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1445 # entries, as well as padding the entry headers. We do a preliminary signing
1446 # (with an incomplete metadata entry) to allow that to happen. Then compute
1447 # the ZIP entry offsets, write back the final metadata and do the final
1448 # signing.
1449 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1450 try:
1451 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1452 except PropertyFiles.InsufficientSpaceException:
1453 # Even with the preliminary signing, the entry orders may change
1454 # dramatically, which leads to insufficiently reserved space during the
1455 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1456 # preliminary signing works, based on the already ordered ZIP entries, to
1457 # address the issue.
1458 prelim_signing = ComputeAllPropertyFiles(
1459 prelim_signing, needed_property_files)
1460 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001461
1462 # Replace the METADATA entry.
1463 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001464 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001465 WriteMetadata(metadata, output_zip)
1466 common.ZipClose(output_zip)
1467
1468 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001469 if OPTIONS.no_signing:
1470 output_file = prelim_signing
1471 else:
1472 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001473
1474 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001475 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001476 for property_files in needed_property_files:
1477 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001478
xunchang1cfe2512019-02-19 14:14:48 -08001479 # If requested, dump the metadata to a separate file.
1480 output_metadata_path = OPTIONS.output_metadata_path
1481 if output_metadata_path:
1482 WriteMetadata(metadata, output_metadata_path)
1483
Tao Baofe5b69a2018-03-02 09:47:43 -08001484
Tao Bao491d7e22018-02-21 13:17:22 -08001485def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001486 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1487 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001488
Tao Bao481bab82017-12-21 11:23:09 -08001489 target_api_version = target_info["recovery_api_version"]
1490 source_api_version = source_info["recovery_api_version"]
1491 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001492 logger.warning(
1493 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001494
Tao Bao481bab82017-12-21 11:23:09 -08001495 script = edify_generator.EdifyGenerator(
1496 source_api_version, target_info, fstab=source_info["fstab"])
1497
1498 if target_info.oem_props or source_info.oem_props:
1499 if not OPTIONS.oem_no_mount:
1500 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001501
Tao Baodf3a48b2018-01-10 16:30:43 -08001502 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001503
Tao Bao491d7e22018-02-21 13:17:22 -08001504 if not OPTIONS.no_signing:
1505 staging_file = common.MakeTempFile(suffix='.zip')
1506 else:
1507 staging_file = output_file
1508
1509 output_zip = zipfile.ZipFile(
1510 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1511
Geremy Condra36bd3652014-02-06 19:45:10 -08001512 device_specific = common.DeviceSpecificParams(
1513 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001514 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001515 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001516 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001517 output_zip=output_zip,
1518 script=script,
1519 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001520 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001521
Geremy Condra36bd3652014-02-06 19:45:10 -08001522 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001523 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001524 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001525 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001526 updating_boot = (not OPTIONS.two_step and
1527 (source_boot.data != target_boot.data))
1528
Geremy Condra36bd3652014-02-06 19:45:10 -08001529 target_recovery = common.GetBootableImage(
1530 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001531
Tao Baoe709b092018-02-07 12:40:00 -08001532 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1533 # shared blocks (i.e. some blocks will show up in multiple files' block
1534 # list). We can only allocate such shared blocks to the first "owner", and
1535 # disable imgdiff for all later occurrences.
1536 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1537 target_info.get('ext4_share_dup_blocks') == "true")
1538 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1539 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001540
1541 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1542 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001543 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001544 allow_shared_blocks,
1545 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001546
Tao Bao0582cb62017-12-21 11:47:01 -08001547 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001548 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001549 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001550
Tao Baof8acad12016-07-07 09:09:58 -07001551 # Check the first block of the source system partition for remount R/W only
1552 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001553 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001554 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001555 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1556 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1557 # b) the blocks listed in block map may not contain all the bytes for a given
1558 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001559 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001560 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1561 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001562 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001563 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001564 version=blockimgdiff_version,
1565 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001566
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001567 if HasVendorPartition(target_zip):
1568 if not HasVendorPartition(source_zip):
1569 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001570 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1571 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001572 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1573 "vendor", 4096, target_info)
1574 vendor_tgt = common.GetSparseImage(
1575 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1576 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001577
1578 # Check first block of vendor partition for remount R/W only if
1579 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001580 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001581 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001582 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001583 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001584 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001585 version=blockimgdiff_version,
1586 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001587 else:
1588 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001589
Tao Baobcd1d162017-08-26 13:10:26 -07001590 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001591 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001592
Tao Bao481bab82017-12-21 11:23:09 -08001593 # Assertions (e.g. device properties check).
1594 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001595 device_specific.IncrementalOTA_Assertions()
1596
1597 # Two-step incremental package strategy (in chronological order,
1598 # which is *not* the order in which the generated script has
1599 # things):
1600 #
1601 # if stage is not "2/3" or "3/3":
1602 # do verification on current system
1603 # write recovery image to boot partition
1604 # set stage to "2/3"
1605 # reboot to boot partition and restart recovery
1606 # else if stage is "2/3":
1607 # write recovery image to recovery partition
1608 # set stage to "3/3"
1609 # reboot to recovery partition and restart recovery
1610 # else:
1611 # (stage must be "3/3")
1612 # perform update:
1613 # patch system files, etc.
1614 # force full install of new boot image
1615 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001616 # complete script normally
1617 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001618
1619 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001620 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001621 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001622 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001623 assert fs.fs_type.upper() == "EMMC", \
1624 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001625 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001626 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1627 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001628if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001629""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001630
1631 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1632 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001633 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001634 script.WriteRawImage("/recovery", "recovery.img")
1635 script.AppendExtra("""
1636set_stage("%(bcb_dev)s", "3/3");
1637reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001638else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001639""" % bcb_dev)
1640
Tao Baod42e97e2016-11-30 12:11:57 -08001641 # Stage 1/3: (a) Verify the current system.
1642 script.Comment("Stage 1/3")
1643
Tao Bao6c55a8a2015-04-08 15:30:27 -07001644 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001645 script.Print("Source: {}".format(source_info.fingerprint))
1646 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001647
Geremy Condra36bd3652014-02-06 19:45:10 -08001648 script.Print("Verifying current system...")
1649
1650 device_specific.IncrementalOTA_VerifyBegin()
1651
Tao Bao481bab82017-12-21 11:23:09 -08001652 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001653
Tao Baod8d14be2016-02-04 14:26:02 -08001654 # Check the required cache size (i.e. stashed blocks).
1655 size = []
1656 if system_diff:
1657 size.append(system_diff.required_cache)
1658 if vendor_diff:
1659 size.append(vendor_diff.required_cache)
1660
Geremy Condra36bd3652014-02-06 19:45:10 -08001661 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001662 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001663 d = common.Difference(target_boot, source_boot)
1664 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001665 if d is None:
1666 include_full_boot = True
1667 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1668 else:
1669 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001670
Tao Bao32fcdab2018-10-12 10:30:39 -07001671 logger.info(
1672 "boot target: %d source: %d diff: %d", target_boot.size,
1673 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001674
Tao Bao51216552018-08-26 11:53:15 -07001675 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001676
Tao Bao51216552018-08-26 11:53:15 -07001677 script.PatchPartitionCheck(
1678 "{}:{}:{}:{}".format(
1679 boot_type, boot_device, target_boot.size, target_boot.sha1),
1680 "{}:{}:{}:{}".format(
1681 boot_type, boot_device, source_boot.size, source_boot.sha1))
1682
Tao Baod8d14be2016-02-04 14:26:02 -08001683 size.append(target_boot.size)
1684
1685 if size:
1686 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001687
1688 device_specific.IncrementalOTA_VerifyEnd()
1689
1690 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001691 # Stage 1/3: (b) Write recovery image to /boot.
1692 _WriteRecoveryImageToBoot(script, output_zip)
1693
Geremy Condra36bd3652014-02-06 19:45:10 -08001694 script.AppendExtra("""
1695set_stage("%(bcb_dev)s", "2/3");
1696reboot_now("%(bcb_dev)s", "");
1697else
1698""" % bcb_dev)
1699
Tao Baod42e97e2016-11-30 12:11:57 -08001700 # Stage 3/3: Make changes.
1701 script.Comment("Stage 3/3")
1702
Jesse Zhao75bcea02015-01-06 10:59:53 -08001703 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001704 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001705 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001706 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001707 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1708 if device_specific_diffs:
1709 assert all(isinstance(diff, common.BlockDifference)
1710 for diff in device_specific_diffs), \
1711 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1712 "BlockDifference objects"
1713 for diff in device_specific_diffs:
1714 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001715
Geremy Condra36bd3652014-02-06 19:45:10 -08001716 script.Comment("---- start making changes here ----")
1717
1718 device_specific.IncrementalOTA_InstallBegin()
1719
Yifan Hong10c530d2018-12-27 17:34:18 -08001720 block_diffs = [system_diff]
1721 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001722 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001723 block_diffs.append(vendor_diff)
1724 progress_dict["vendor"] = 0.1
1725 if device_specific_diffs:
1726 block_diffs += device_specific_diffs
1727
1728 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1729 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1730 raise RuntimeError(
1731 "can't generate incremental that disables dynamic partitions")
1732 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1733 info_dict=OPTIONS.target_info_dict,
1734 source_info_dict=OPTIONS.source_info_dict,
1735 block_diffs=block_diffs,
1736 progress_dict=progress_dict)
1737 dynamic_partitions_diff.WriteScript(
1738 script, output_zip, write_verify_script=OPTIONS.verify)
1739 else:
1740 for block_diff in block_diffs:
1741 block_diff.WriteScript(script, output_zip,
1742 progress=progress_dict.get(block_diff.partition),
1743 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001744
1745 if OPTIONS.two_step:
1746 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1747 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001748 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001749
1750 if not OPTIONS.two_step:
1751 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001752 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001753 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001754 script.Print("Installing boot image...")
1755 script.WriteRawImage("/boot", "boot.img")
1756 else:
1757 # Produce the boot image by applying a patch to the current
1758 # contents of the boot partition, and write it back to the
1759 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001760 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001761 script.Print("Patching boot image...")
1762 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001763 script.PatchPartition(
1764 '{}:{}:{}:{}'.format(
1765 boot_type, boot_device, target_boot.size, target_boot.sha1),
1766 '{}:{}:{}:{}'.format(
1767 boot_type, boot_device, source_boot.size, source_boot.sha1),
1768 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001769 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001770 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001771
1772 # Do device-specific installation (eg, write radio image).
1773 device_specific.IncrementalOTA_InstallEnd()
1774
1775 if OPTIONS.extra_script is not None:
1776 script.AppendExtra(OPTIONS.extra_script)
1777
Doug Zongker922206e2014-03-04 13:16:24 -08001778 if OPTIONS.wipe_user_data:
1779 script.Print("Erasing user data...")
1780 script.FormatPartition("/data")
1781
Geremy Condra36bd3652014-02-06 19:45:10 -08001782 if OPTIONS.two_step:
1783 script.AppendExtra("""
1784set_stage("%(bcb_dev)s", "");
1785endif;
1786endif;
1787""" % bcb_dev)
1788
1789 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001790 # For downgrade OTAs, we prefer to use the update-binary in the source
1791 # build that is actually newer than the one in the target build.
1792 if OPTIONS.downgrade:
1793 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1794 else:
1795 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001796 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001797
1798 # We haven't written the metadata entry yet, which will be handled in
1799 # FinalizeMetadata().
1800 common.ZipClose(output_zip)
1801
1802 # Sign the generated zip package unless no_signing is specified.
1803 needed_property_files = (
1804 NonAbOtaPropertyFiles(),
1805 )
1806 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001807
Doug Zongker32b527d2014-03-04 10:03:02 -08001808
Tao Bao15a146a2018-02-21 16:06:59 -08001809def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001810 """Returns a target-files.zip file for generating secondary payload.
1811
1812 Although the original target-files.zip already contains secondary slot
1813 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1814 ones without _other suffix. Note that we cannot instead modify the names in
1815 META/ab_partitions.txt, because there are no matching partitions on device.
1816
1817 For the partitions that don't have secondary images, the ones for primary
1818 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1819 bootloader images in the inactive slot.
1820
1821 Args:
1822 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001823 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001824
1825 Returns:
1826 The filename of the target-files.zip for generating secondary payload.
1827 """
1828 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1829 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1830
Tao Baodba59ee2018-01-09 13:21:02 -08001831 with zipfile.ZipFile(input_file, 'r') as input_zip:
1832 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001833 namelist = input_zip.namelist()
1834
Tao Bao0ff15de2019-03-20 11:26:06 -07001835 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001836 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001837 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1838 if info.filename == 'IMAGES/system_other.img':
1839 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1840
1841 # Primary images and friends need to be skipped explicitly.
1842 elif info.filename in ('IMAGES/system.img',
1843 'IMAGES/system.map'):
1844 pass
1845
Tao Bao15a146a2018-02-21 16:06:59 -08001846 # Skip copying the postinstall config if requested.
1847 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1848 pass
1849
Tao Bao12489802018-07-12 14:47:38 -07001850 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001851 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1852
Tao Baof7140c02018-01-30 17:09:24 -08001853 common.ZipClose(target_zip)
1854
1855 return target_file
1856
1857
Tao Bao15a146a2018-02-21 16:06:59 -08001858def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1859 """Returns a target-files.zip that's not containing postinstall_config.txt.
1860
1861 This allows brillo_update_payload script to skip writing all the postinstall
1862 hooks in the generated payload. The input target-files.zip file will be
1863 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1864 contain the postinstall_config.txt entry, the input file will be returned.
1865
1866 Args:
1867 input_file: The input target-files.zip filename.
1868
1869 Returns:
1870 The filename of target-files.zip that doesn't contain postinstall config.
1871 """
1872 # We should only make a copy if postinstall_config entry exists.
1873 with zipfile.ZipFile(input_file, 'r') as input_zip:
1874 if POSTINSTALL_CONFIG not in input_zip.namelist():
1875 return input_file
1876
1877 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1878 shutil.copyfile(input_file, target_file)
1879 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1880 return target_file
1881
1882
Yifan Hong50e79542018-11-08 17:44:12 -08001883def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001884 super_block_devices,
1885 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001886 """Returns a target-files.zip for retrofitting dynamic partitions.
1887
1888 This allows brillo_update_payload to generate an OTA based on the exact
1889 bits on the block devices. Postinstall is disabled.
1890
1891 Args:
1892 input_file: The input target-files.zip filename.
1893 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001894 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001895
1896 Returns:
1897 The filename of target-files.zip with *.img replaced with super_*.img for
1898 each block device in super_block_devices.
1899 """
1900 assert super_block_devices, "No super_block_devices are specified."
1901
1902 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001903 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001904
1905 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1906 shutil.copyfile(input_file, target_file)
1907
1908 with zipfile.ZipFile(input_file, 'r') as input_zip:
1909 namelist = input_zip.namelist()
1910
Yifan Hongb433eba2019-03-06 12:42:53 -08001911 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1912
1913 # Remove partitions from META/ab_partitions.txt that is in
1914 # dynamic_partition_list but not in super_block_devices so that
1915 # brillo_update_payload won't generate update for those logical partitions.
1916 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1917 with open(ab_partitions_file) as f:
1918 ab_partitions_lines = f.readlines()
1919 ab_partitions = [line.strip() for line in ab_partitions_lines]
1920 # Assert that all super_block_devices are in ab_partitions
1921 super_device_not_updated = [partition for partition in super_block_devices
1922 if partition not in ab_partitions]
1923 assert not super_device_not_updated, \
1924 "{} is in super_block_devices but not in {}".format(
1925 super_device_not_updated, AB_PARTITIONS)
1926 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1927 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1928 with open(new_ab_partitions, 'w') as f:
1929 for partition in ab_partitions:
1930 if (partition in dynamic_partition_list and
1931 partition not in super_block_devices):
1932 logger.info("Dropping %s from ab_partitions.txt", partition)
1933 continue
1934 f.write(partition + "\n")
1935 to_delete = [AB_PARTITIONS]
1936
Yifan Hong50e79542018-11-08 17:44:12 -08001937 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001938 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001939
1940 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1941 # is a regular update on devices without dynamic partitions support.
1942 to_delete += [DYNAMIC_PARTITION_INFO]
1943
Tao Bao03fecb62018-11-28 10:59:23 -08001944 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001945 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001946 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001947
1948 common.ZipDelete(target_file, to_delete)
1949
Yifan Hong50e79542018-11-08 17:44:12 -08001950 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1951
1952 # Write super_{foo}.img as {foo}.img.
1953 for src, dst in replace.items():
1954 assert src in namelist, \
1955 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1956 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1957 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1958
Yifan Hongb433eba2019-03-06 12:42:53 -08001959 # Write new ab_partitions.txt file
1960 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1961
Yifan Hong50e79542018-11-08 17:44:12 -08001962 common.ZipClose(target_zip)
1963
1964 return target_file
1965
1966
Tao Baoc098e9e2016-01-07 13:03:56 -08001967def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1968 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001969 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001970 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001971 if not OPTIONS.no_signing:
1972 staging_file = common.MakeTempFile(suffix='.zip')
1973 else:
1974 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001975 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001976 compression=zipfile.ZIP_DEFLATED)
1977
Tao Bao481bab82017-12-21 11:23:09 -08001978 if source_file is not None:
1979 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1980 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1981 else:
1982 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1983 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001984
Tao Bao481bab82017-12-21 11:23:09 -08001985 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001986 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001987
Yifan Hong50e79542018-11-08 17:44:12 -08001988 if OPTIONS.retrofit_dynamic_partitions:
1989 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001990 target_file, target_info.get("super_block_devices").strip().split(),
1991 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001992 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001993 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1994
Tao Bao40b18822018-01-30 18:19:04 -08001995 # Generate payload.
1996 payload = Payload()
1997
1998 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001999 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08002000 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07002001 else:
2002 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08002003 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08002004
Tao Bao40b18822018-01-30 18:19:04 -08002005 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08002006
Tao Bao40b18822018-01-30 18:19:04 -08002007 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08002008 payload_signer = PayloadSigner()
2009 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08002010
Tao Bao40b18822018-01-30 18:19:04 -08002011 # Write the payload into output zip.
2012 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002013
Tao Baof7140c02018-01-30 17:09:24 -08002014 # Generate and include the secondary payload that installs secondary images
2015 # (e.g. system_other.img).
2016 if OPTIONS.include_secondary:
2017 # We always include a full payload for the secondary slot, even when
2018 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08002019 secondary_target_file = GetTargetFilesZipForSecondaryImages(
2020 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08002021 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08002022 secondary_payload.Generate(secondary_target_file,
2023 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08002024 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08002025 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08002026
Tianjie Xucfa86222016-03-07 16:31:19 -08002027 # If dm-verity is supported for the device, copy contents of care_map
2028 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07002029 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08002030 if (target_info.get("verity") == "true" or
2031 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002032 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
2033 "META/" + x in target_zip.namelist()]
2034
2035 # Adds care_map if either the protobuf format or the plain text one exists.
2036 if care_map_list:
2037 care_map_name = care_map_list[0]
2038 care_map_data = target_zip.read("META/" + care_map_name)
2039 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08002040 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002041 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002042 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002043 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002044 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002045
Tao Baobcd1d162017-08-26 13:10:26 -07002046 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08002047 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07002048
Tao Bao21803d32017-04-19 10:16:09 -07002049 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002050
Tao Baofe5b69a2018-03-02 09:47:43 -08002051 # We haven't written the metadata entry yet, which will be handled in
2052 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002053 common.ZipClose(output_zip)
2054
Tao Bao85f16982018-03-08 16:28:33 -08002055 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2056 # all the info of the latter. However, system updaters and OTA servers need to
2057 # take time to switch to the new flag. We keep both of the flags for
2058 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002059 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002060 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002061 StreamingPropertyFiles(),
2062 )
2063 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002064
Tao Baoc098e9e2016-01-07 13:03:56 -08002065
Doug Zongkereef39442009-04-02 12:14:19 -07002066def main(argv):
2067
2068 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002069 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002070 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002071 elif o in ("-i", "--incremental_from"):
2072 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002073 elif o == "--full_radio":
2074 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002075 elif o == "--full_bootloader":
2076 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002077 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002078 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002079 elif o == "--downgrade":
2080 OPTIONS.downgrade = True
2081 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002082 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002083 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002084 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002085 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002086 elif o == "--oem_no_mount":
2087 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002088 elif o in ("-e", "--extra_script"):
2089 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002090 elif o in ("-t", "--worker_threads"):
2091 if a.isdigit():
2092 OPTIONS.worker_threads = int(a)
2093 else:
2094 raise ValueError("Cannot parse value %r for option %r - only "
2095 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002096 elif o in ("-2", "--two_step"):
2097 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002098 elif o == "--include_secondary":
2099 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002100 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002101 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002102 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002103 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002104 elif o == "--block":
2105 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002106 elif o in ("-b", "--binary"):
2107 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002108 elif o == "--stash_threshold":
2109 try:
2110 OPTIONS.stash_threshold = float(a)
2111 except ValueError:
2112 raise ValueError("Cannot parse value %r for option %r - expecting "
2113 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002114 elif o == "--log_diff":
2115 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002116 elif o == "--payload_signer":
2117 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002118 elif o == "--payload_signer_args":
2119 OPTIONS.payload_signer_args = shlex.split(a)
xunchang376cc7c2019-04-08 23:04:58 -07002120 elif o == "--payload_signer_key_size":
2121 OPTIONS.payload_signer_key_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002122 elif o == "--extracted_input_target_files":
2123 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002124 elif o == "--skip_postinstall":
2125 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002126 elif o == "--retrofit_dynamic_partitions":
2127 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002128 elif o == "--skip_compatibility_check":
2129 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002130 elif o == "--output_metadata_path":
2131 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002132 else:
2133 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002134 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002135
2136 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002137 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002138 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002139 "package_key=",
2140 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002141 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002142 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002143 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002144 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002145 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002146 "extra_script=",
2147 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002148 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002149 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002150 "no_signing",
2151 "block",
2152 "binary=",
2153 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002154 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002155 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002156 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002157 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002158 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002159 "payload_signer_args=",
xunchang376cc7c2019-04-08 23:04:58 -07002160 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002161 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002162 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002163 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002164 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002165 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002166 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002167
2168 if len(args) != 2:
2169 common.Usage(__doc__)
2170 sys.exit(1)
2171
Tao Bao32fcdab2018-10-12 10:30:39 -07002172 common.InitLogging()
2173
Tao Bao5d182562016-02-23 11:38:39 -08002174 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002175 # We should only allow downgrading incrementals (as opposed to full).
2176 # Otherwise the device may go back from arbitrary build with this full
2177 # OTA package.
2178 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002179 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002180
Tao Bao2db13852018-01-08 22:28:57 -08002181 # Load the build info dicts from the zip directly or the extracted input
2182 # directory. We don't need to unzip the entire target-files zips, because they
2183 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2184 # When loading the info dicts, we don't need to provide the second parameter
2185 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2186 # some properties with their actual paths, such as 'selinux_fc',
2187 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002188 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002189 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002190 else:
Tao Bao2db13852018-01-08 22:28:57 -08002191 with zipfile.ZipFile(args[0], 'r') as input_zip:
2192 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002193
Tao Bao32fcdab2018-10-12 10:30:39 -07002194 logger.info("--- target info ---")
2195 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002196
2197 # Load the source build dict if applicable.
2198 if OPTIONS.incremental_source is not None:
2199 OPTIONS.target_info_dict = OPTIONS.info_dict
2200 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2201 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2202
Tao Bao32fcdab2018-10-12 10:30:39 -07002203 logger.info("--- source info ---")
2204 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002205
2206 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002207 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2208
Yifan Hong50e79542018-11-08 17:44:12 -08002209 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002210 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002211 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002212 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2213 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002214 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2215 raise common.ExternalError(
2216 "Expect to generate incremental OTA for retrofitting dynamic "
2217 "partitions, but dynamic_partition_retrofit is not set in target "
2218 "build.")
2219 logger.info("Implicitly generating retrofit incremental OTA.")
2220 OPTIONS.retrofit_dynamic_partitions = True
2221
2222 # Skip postinstall for retrofitting dynamic partitions.
2223 if OPTIONS.retrofit_dynamic_partitions:
2224 OPTIONS.skip_postinstall = True
2225
Tao Baoc098e9e2016-01-07 13:03:56 -08002226 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2227
Christian Oderf63e2cd2017-05-01 22:30:15 +02002228 # Use the default key to sign the package if not specified with package_key.
2229 # package_keys are needed on ab_updates, so always define them if an
2230 # ab_update is getting created.
2231 if not OPTIONS.no_signing or ab_update:
2232 if OPTIONS.package_key is None:
2233 OPTIONS.package_key = OPTIONS.info_dict.get(
2234 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002235 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002236 # Get signing keys
2237 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2238
Tao Baoc098e9e2016-01-07 13:03:56 -08002239 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002240 WriteABOTAPackageWithBrilloScript(
2241 target_file=args[0],
2242 output_file=args[1],
2243 source_file=OPTIONS.incremental_source)
2244
Tao Bao32fcdab2018-10-12 10:30:39 -07002245 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002246 return
2247
Tao Bao2db13852018-01-08 22:28:57 -08002248 # Sanity check the loaded info dicts first.
2249 if OPTIONS.info_dict.get("no_recovery") == "true":
2250 raise common.ExternalError(
2251 "--- target build has specified no recovery ---")
2252
2253 # Non-A/B OTAs rely on /cache partition to store temporary files.
2254 cache_size = OPTIONS.info_dict.get("cache_size")
2255 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002256 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002257 OPTIONS.cache_size = cache_size
2258
Doug Zongker1c390a22009-05-14 19:06:36 -07002259 if OPTIONS.extra_script is not None:
2260 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2261
Dan Willemsencea5cd22017-03-21 14:44:27 -07002262 if OPTIONS.extracted_input is not None:
2263 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002264 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002265 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002266 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002267 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002268
Tao Bao2db13852018-01-08 22:28:57 -08002269 # If the caller explicitly specified the device-specific extensions path via
2270 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2271 # is present in the target target_files. Otherwise, take the path of the file
2272 # from 'tool_extensions' in the info dict and look for that in the local
2273 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002274 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002275 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2276 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002277 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002278 OPTIONS.device_specific = from_input
2279 else:
Tao Bao2db13852018-01-08 22:28:57 -08002280 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002281
Doug Zongker37974732010-09-16 17:44:38 -07002282 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002283 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002284
Tao Bao767e3ac2015-11-10 12:19:19 -08002285 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002286 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002287 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002288 WriteFullOTAPackage(
2289 input_zip,
2290 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002291
Tao Bao32b80dc2018-01-08 22:50:47 -08002292 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002293 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002294 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002295 OPTIONS.source_tmp = common.UnzipTemp(
2296 OPTIONS.incremental_source, UNZIP_PATTERN)
2297 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2298 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002299 WriteBlockIncrementalOTAPackage(
2300 input_zip,
2301 source_zip,
2302 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002303
2304 if OPTIONS.log_diff:
2305 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002306 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002307 target_files_diff.recursiveDiff(
2308 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002309
Tao Bao32fcdab2018-10-12 10:30:39 -07002310 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002311
2312
2313if __name__ == '__main__':
2314 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002315 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002316 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002317 except common.ExternalError:
2318 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002319 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002320 finally:
2321 common.Cleanup()