blob: dd3e19030b5d489adb2b9d4b702f1885df2e0de1 [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
Yifan Hong10c530d2018-12-27 17:34:18 -0800920 def GetBlockDifference(partition):
921 # Full OTA is done as an "incremental" against an empty source image. This
922 # has the effect of writing new data from the package to the entire
923 # partition, but lets us reuse the updater code that writes incrementals to
924 # do it.
Yifan Hong8a66a712019-04-04 15:37:57 -0700925 tgt = common.GetUserImage(partition, OPTIONS.input_tmp, input_zip,
926 info_dict=target_info,
927 reset_file_map=True)
Yifan Hong10c530d2018-12-27 17:34:18 -0800928 diff = common.BlockDifference(partition, tgt, src=None)
929 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700930
Yifan Hong10c530d2018-12-27 17:34:18 -0800931 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
932 if device_specific_diffs:
933 assert all(isinstance(diff, common.BlockDifference)
934 for diff in device_specific_diffs), \
935 "FullOTA_GetBlockDifferences is not returning a list of " \
936 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800937
Yifan Hong10c530d2018-12-27 17:34:18 -0800938 progress_dict = dict()
939 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700940 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800941 block_diffs.append(GetBlockDifference("vendor"))
942 progress_dict["vendor"] = 0.1
943 if device_specific_diffs:
944 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700945
Yifan Hong10c530d2018-12-27 17:34:18 -0800946 if target_info.get('use_dynamic_partitions') == "true":
947 # Use empty source_info_dict to indicate that all partitions / groups must
948 # be re-added.
949 dynamic_partitions_diff = common.DynamicPartitionsDifference(
950 info_dict=OPTIONS.info_dict,
951 block_diffs=block_diffs,
952 progress_dict=progress_dict)
953 dynamic_partitions_diff.WriteScript(script, output_zip,
954 write_verify_script=OPTIONS.verify)
955 else:
956 for block_diff in block_diffs:
957 block_diff.WriteScript(script, output_zip,
958 progress=progress_dict.get(block_diff.partition),
959 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700960
Tao Bao481bab82017-12-21 11:23:09 -0800961 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700962
Yifan Hong10c530d2018-12-27 17:34:18 -0800963 boot_img = common.GetBootableImage(
964 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800965 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700966 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700967
Doug Zongker01ce19c2014-02-04 13:48:15 -0800968 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700969 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700970
Doug Zongker01ce19c2014-02-04 13:48:15 -0800971 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700972 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700973
Doug Zongker1c390a22009-05-14 19:06:36 -0700974 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700975 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700976
Doug Zongker14833602010-02-02 13:12:04 -0800977 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800978
Doug Zongker922206e2014-03-04 13:16:24 -0800979 if OPTIONS.wipe_user_data:
980 script.ShowProgress(0.1, 10)
981 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700982
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800983 if OPTIONS.two_step:
984 script.AppendExtra("""
985set_stage("%(bcb_dev)s", "");
986""" % bcb_dev)
987 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800988
989 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
990 script.Comment("Stage 1/3")
991 _WriteRecoveryImageToBoot(script, output_zip)
992
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800993 script.AppendExtra("""
994set_stage("%(bcb_dev)s", "2/3");
995reboot_now("%(bcb_dev)s", "");
996endif;
997endif;
998""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800999
Tao Bao5d182562016-02-23 11:38:39 -08001000 script.SetProgress(1)
1001 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001002 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001003
1004 # We haven't written the metadata entry, which will be done in
1005 # FinalizeMetadata.
1006 common.ZipClose(output_zip)
1007
1008 needed_property_files = (
1009 NonAbOtaPropertyFiles(),
1010 )
1011 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -07001012
Doug Zongkerfc44a512014-08-26 13:10:25 -07001013
xunchang1cfe2512019-02-19 14:14:48 -08001014def WriteMetadata(metadata, output):
1015 """Writes the metadata to the zip archive or a file.
1016
1017 Args:
1018 metadata: The metadata dict for the package.
1019 output: A ZipFile object or a string of the output file path.
1020 """
1021
Tao Bao2dd1c482017-02-03 16:49:39 -08001022 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
xunchang1cfe2512019-02-19 14:14:48 -08001023 if isinstance(output, zipfile.ZipFile):
1024 common.ZipWriteStr(output, METADATA_NAME, value,
1025 compress_type=zipfile.ZIP_STORED)
1026 return
1027
1028 with open(output, 'w') as f:
1029 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001030
Doug Zongkerfc44a512014-08-26 13:10:25 -07001031
Tao Bao481bab82017-12-21 11:23:09 -08001032def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001033 # Only incremental OTAs are allowed to reach here.
1034 assert OPTIONS.incremental_source is not None
1035
Tao Bao481bab82017-12-21 11:23:09 -08001036 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1037 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -08001038 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1039
1040 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001041 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001042 raise RuntimeError(
1043 "--downgrade or --override_timestamp specified but no downgrade "
1044 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001045 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001046 else:
1047 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001048 raise RuntimeError(
1049 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1050 "Need to specify --override_timestamp OR --downgrade to allow "
1051 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001052
1053
Tao Baodf3a48b2018-01-10 16:30:43 -08001054def GetPackageMetadata(target_info, source_info=None):
1055 """Generates and returns the metadata dict.
1056
1057 It generates a dict() that contains the info to be written into an OTA
1058 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001059 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001060
1061 Args:
1062 target_info: The BuildInfo instance that holds the target build info.
1063 source_info: The BuildInfo instance that holds the source build info, or
1064 None if generating full OTA.
1065
1066 Returns:
1067 A dict to be written into package metadata entry.
1068 """
1069 assert isinstance(target_info, BuildInfo)
1070 assert source_info is None or isinstance(source_info, BuildInfo)
1071
1072 metadata = {
1073 'post-build' : target_info.fingerprint,
1074 'post-build-incremental' : target_info.GetBuildProp(
1075 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001076 'post-sdk-level' : target_info.GetBuildProp(
1077 'ro.build.version.sdk'),
1078 'post-security-patch-level' : target_info.GetBuildProp(
1079 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001080 }
1081
1082 if target_info.is_ab:
1083 metadata['ota-type'] = 'AB'
1084 metadata['ota-required-cache'] = '0'
1085 else:
1086 metadata['ota-type'] = 'BLOCK'
1087
1088 if OPTIONS.wipe_user_data:
1089 metadata['ota-wipe'] = 'yes'
1090
Tao Bao393eeb42019-03-06 16:00:38 -08001091 if OPTIONS.retrofit_dynamic_partitions:
1092 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1093
Tao Baodf3a48b2018-01-10 16:30:43 -08001094 is_incremental = source_info is not None
1095 if is_incremental:
1096 metadata['pre-build'] = source_info.fingerprint
1097 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1098 'ro.build.version.incremental')
1099 metadata['pre-device'] = source_info.device
1100 else:
1101 metadata['pre-device'] = target_info.device
1102
Tao Baofaa8e0b2018-04-12 14:31:43 -07001103 # Use the actual post-timestamp, even for a downgrade case.
1104 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1105
1106 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001107 if is_incremental:
1108 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001109
1110 return metadata
1111
1112
Tao Baod3fc38a2018-03-08 16:09:01 -08001113class PropertyFiles(object):
1114 """A class that computes the property-files string for an OTA package.
1115
1116 A property-files string is a comma-separated string that contains the
1117 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1118 can be fetched directly with the package URL along with the offset/size info.
1119 These strings can be used for streaming A/B OTAs, or allowing an updater to
1120 download package metadata entry directly, without paying the cost of
1121 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001122
Tao Baocc8e2662018-03-01 19:30:00 -08001123 Computing the final property-files string requires two passes. Because doing
1124 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1125 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1126 values.
1127
1128 This class provides functions to be called for each pass. The general flow is
1129 as follows.
1130
Tao Baod3fc38a2018-03-08 16:09:01 -08001131 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001132 # The first pass, which writes placeholders before doing initial signing.
1133 property_files.Compute()
1134 SignOutput()
1135
1136 # The second pass, by replacing the placeholders with actual data.
1137 property_files.Finalize()
1138 SignOutput()
1139
1140 And the caller can additionally verify the final result.
1141
1142 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001143 """
1144
Tao Baocc8e2662018-03-01 19:30:00 -08001145 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001146 self.name = None
1147 self.required = ()
1148 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001149
Tao Baocc8e2662018-03-01 19:30:00 -08001150 def Compute(self, input_zip):
1151 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001152
Tao Baocc8e2662018-03-01 19:30:00 -08001153 We reserve extra space for the offset and size of the metadata entry itself,
1154 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001155
Tao Baocc8e2662018-03-01 19:30:00 -08001156 Args:
1157 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001158
Tao Baocc8e2662018-03-01 19:30:00 -08001159 Returns:
1160 A string with placeholders for the metadata offset/size info, e.g.
1161 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1162 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001163 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001164
Tao Baod2ce2ed2018-03-16 12:59:42 -07001165 class InsufficientSpaceException(Exception):
1166 pass
1167
Tao Baocc8e2662018-03-01 19:30:00 -08001168 def Finalize(self, input_zip, reserved_length):
1169 """Finalizes a property-files string with actual METADATA offset/size info.
1170
1171 The input ZIP file has been signed, with the ZIP entries in the desired
1172 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1173 the ZIP entry offsets and construct the property-files string with actual
1174 data. Note that during this process, we must pad the property-files string
1175 to the reserved length, so that the METADATA entry size remains the same.
1176 Otherwise the entries' offsets and sizes may change again.
1177
1178 Args:
1179 input_zip: The input ZIP file.
1180 reserved_length: The reserved length of the property-files string during
1181 the call to Compute(). The final string must be no more than this
1182 size.
1183
1184 Returns:
1185 A property-files string including the metadata offset/size info, e.g.
1186 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1187
1188 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001189 InsufficientSpaceException: If the reserved length is insufficient to hold
1190 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001191 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001192 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001193 if len(result) > reserved_length:
1194 raise self.InsufficientSpaceException(
1195 'Insufficient reserved space: reserved={}, actual={}'.format(
1196 reserved_length, len(result)))
1197
Tao Baocc8e2662018-03-01 19:30:00 -08001198 result += ' ' * (reserved_length - len(result))
1199 return result
1200
1201 def Verify(self, input_zip, expected):
1202 """Verifies the input ZIP file contains the expected property-files string.
1203
1204 Args:
1205 input_zip: The input ZIP file.
1206 expected: The property-files string that's computed from Finalize().
1207
1208 Raises:
1209 AssertionError: On finding a mismatch.
1210 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001211 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001212 assert actual == expected, \
1213 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1214
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001215 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1216 """
1217 Constructs the property-files string per request.
1218
1219 Args:
1220 zip_file: The input ZIP file.
1221 reserved_length: The reserved length of the property-files string.
1222
1223 Returns:
1224 A property-files string including the metadata offset/size info, e.g.
1225 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1226 """
Tao Baocc8e2662018-03-01 19:30:00 -08001227
1228 def ComputeEntryOffsetSize(name):
1229 """Computes the zip entry offset and size."""
1230 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001231 offset = info.header_offset
1232 offset += zipfile.sizeFileHeader
1233 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001234 size = info.file_size
1235 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1236
1237 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001238 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001239 for entry in self.required:
1240 tokens.append(ComputeEntryOffsetSize(entry))
1241 for entry in self.optional:
1242 if entry in zip_file.namelist():
1243 tokens.append(ComputeEntryOffsetSize(entry))
1244
1245 # 'META-INF/com/android/metadata' is required. We don't know its actual
1246 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001247 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1248 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1249 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1250 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001251 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001252 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001253 else:
1254 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1255
1256 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001257
Tao Bao85f16982018-03-08 16:28:33 -08001258 def _GetPrecomputed(self, input_zip):
1259 """Computes the additional tokens to be included into the property-files.
1260
1261 This applies to tokens without actual ZIP entries, such as
1262 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1263 that they can download the payload metadata directly with the info.
1264
1265 Args:
1266 input_zip: The input zip file.
1267
1268 Returns:
1269 A list of strings (tokens) to be added to the property-files string.
1270 """
1271 # pylint: disable=no-self-use
1272 # pylint: disable=unused-argument
1273 return []
1274
Tao Baofe5b69a2018-03-02 09:47:43 -08001275
Tao Baod3fc38a2018-03-08 16:09:01 -08001276class StreamingPropertyFiles(PropertyFiles):
1277 """A subclass for computing the property-files for streaming A/B OTAs."""
1278
1279 def __init__(self):
1280 super(StreamingPropertyFiles, self).__init__()
1281 self.name = 'ota-streaming-property-files'
1282 self.required = (
1283 # payload.bin and payload_properties.txt must exist.
1284 'payload.bin',
1285 'payload_properties.txt',
1286 )
1287 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001288 # care_map is available only if dm-verity is enabled.
1289 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001290 'care_map.txt',
1291 # compatibility.zip is available only if target supports Treble.
1292 'compatibility.zip',
1293 )
1294
1295
Tao Bao85f16982018-03-08 16:28:33 -08001296class AbOtaPropertyFiles(StreamingPropertyFiles):
1297 """The property-files for A/B OTA that includes payload_metadata.bin info.
1298
1299 Since P, we expose one more token (aka property-file), in addition to the ones
1300 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1301 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1302 doesn't exist as a separate ZIP entry, but can be used to verify if the
1303 payload can be applied on the given device.
1304
1305 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1306 and the newly added 'ota-property-files' in P. The new token will only be
1307 available in 'ota-property-files'.
1308 """
1309
1310 def __init__(self):
1311 super(AbOtaPropertyFiles, self).__init__()
1312 self.name = 'ota-property-files'
1313
1314 def _GetPrecomputed(self, input_zip):
1315 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1316 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1317
1318 @staticmethod
1319 def _GetPayloadMetadataOffsetAndSize(input_zip):
1320 """Computes the offset and size of the payload metadata for a given package.
1321
1322 (From system/update_engine/update_metadata.proto)
1323 A delta update file contains all the deltas needed to update a system from
1324 one specific version to another specific version. The update format is
1325 represented by this struct pseudocode:
1326
1327 struct delta_update_file {
1328 char magic[4] = "CrAU";
1329 uint64 file_format_version;
1330 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1331
1332 // Only present if format_version > 1:
1333 uint32 metadata_signature_size;
1334
1335 // The Bzip2 compressed DeltaArchiveManifest
1336 char manifest[metadata_signature_size];
1337
1338 // The signature of the metadata (from the beginning of the payload up to
1339 // this location, not including the signature itself). This is a
1340 // serialized Signatures message.
1341 char medatada_signature_message[metadata_signature_size];
1342
1343 // Data blobs for files, no specific format. The specific offset
1344 // and length of each data blob is recorded in the DeltaArchiveManifest.
1345 struct {
1346 char data[];
1347 } blobs[];
1348
1349 // These two are not signed:
1350 uint64 payload_signatures_message_size;
1351 char payload_signatures_message[];
1352 };
1353
1354 'payload-metadata.bin' contains all the bytes from the beginning of the
1355 payload, till the end of 'medatada_signature_message'.
1356 """
1357 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001358 payload_offset = payload_info.header_offset
1359 payload_offset += zipfile.sizeFileHeader
1360 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001361 payload_size = payload_info.file_size
1362
1363 with input_zip.open('payload.bin', 'r') as payload_fp:
1364 header_bin = payload_fp.read(24)
1365
1366 # network byte order (big-endian)
1367 header = struct.unpack("!IQQL", header_bin)
1368
1369 # 'CrAU'
1370 magic = header[0]
1371 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1372
1373 manifest_size = header[2]
1374 metadata_signature_size = header[3]
1375 metadata_total = 24 + manifest_size + metadata_signature_size
1376 assert metadata_total < payload_size
1377
1378 return (payload_offset, metadata_total)
1379
1380
Tao Bao491d7e22018-02-21 13:17:22 -08001381class NonAbOtaPropertyFiles(PropertyFiles):
1382 """The property-files for non-A/B OTA.
1383
1384 For non-A/B OTA, the property-files string contains the info for METADATA
1385 entry, with which a system updater can be fetched the package metadata prior
1386 to downloading the entire package.
1387 """
1388
1389 def __init__(self):
1390 super(NonAbOtaPropertyFiles, self).__init__()
1391 self.name = 'ota-property-files'
1392
1393
Tao Baod3fc38a2018-03-08 16:09:01 -08001394def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001395 """Finalizes the metadata and signs an A/B OTA package.
1396
1397 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1398 that contains the offsets and sizes for the ZIP entries. An example
1399 property-files string is as follows.
1400
1401 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1402
1403 OTA server can pass down this string, in addition to the package URL, to the
1404 system update client. System update client can then fetch individual ZIP
1405 entries (ZIP_STORED) directly at the given offset of the URL.
1406
1407 Args:
1408 metadata: The metadata dict for the package.
1409 input_file: The input ZIP filename that doesn't contain the package METADATA
1410 entry yet.
1411 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001412 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001413 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001414
Tao Baod2ce2ed2018-03-16 12:59:42 -07001415 def ComputeAllPropertyFiles(input_file, needed_property_files):
1416 # Write the current metadata entry with placeholders.
1417 with zipfile.ZipFile(input_file) as input_zip:
1418 for property_files in needed_property_files:
1419 metadata[property_files.name] = property_files.Compute(input_zip)
1420 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001421
Tao Baod2ce2ed2018-03-16 12:59:42 -07001422 if METADATA_NAME in namelist:
1423 common.ZipDelete(input_file, METADATA_NAME)
1424 output_zip = zipfile.ZipFile(input_file, 'a')
1425 WriteMetadata(metadata, output_zip)
1426 common.ZipClose(output_zip)
1427
1428 if OPTIONS.no_signing:
1429 return input_file
1430
Tao Bao491d7e22018-02-21 13:17:22 -08001431 prelim_signing = common.MakeTempFile(suffix='.zip')
1432 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001433 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001434
Tao Baod2ce2ed2018-03-16 12:59:42 -07001435 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1436 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1437 for property_files in needed_property_files:
1438 metadata[property_files.name] = property_files.Finalize(
1439 prelim_signing_zip, len(metadata[property_files.name]))
1440
1441 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1442 # entries, as well as padding the entry headers. We do a preliminary signing
1443 # (with an incomplete metadata entry) to allow that to happen. Then compute
1444 # the ZIP entry offsets, write back the final metadata and do the final
1445 # signing.
1446 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1447 try:
1448 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1449 except PropertyFiles.InsufficientSpaceException:
1450 # Even with the preliminary signing, the entry orders may change
1451 # dramatically, which leads to insufficiently reserved space during the
1452 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1453 # preliminary signing works, based on the already ordered ZIP entries, to
1454 # address the issue.
1455 prelim_signing = ComputeAllPropertyFiles(
1456 prelim_signing, needed_property_files)
1457 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001458
1459 # Replace the METADATA entry.
1460 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001461 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001462 WriteMetadata(metadata, output_zip)
1463 common.ZipClose(output_zip)
1464
1465 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001466 if OPTIONS.no_signing:
1467 output_file = prelim_signing
1468 else:
1469 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001470
1471 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001472 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001473 for property_files in needed_property_files:
1474 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001475
xunchang1cfe2512019-02-19 14:14:48 -08001476 # If requested, dump the metadata to a separate file.
1477 output_metadata_path = OPTIONS.output_metadata_path
1478 if output_metadata_path:
1479 WriteMetadata(metadata, output_metadata_path)
1480
Tao Baofe5b69a2018-03-02 09:47:43 -08001481
Tao Bao491d7e22018-02-21 13:17:22 -08001482def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001483 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1484 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001485
Tao Bao481bab82017-12-21 11:23:09 -08001486 target_api_version = target_info["recovery_api_version"]
1487 source_api_version = source_info["recovery_api_version"]
1488 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001489 logger.warning(
1490 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001491
Tao Bao481bab82017-12-21 11:23:09 -08001492 script = edify_generator.EdifyGenerator(
1493 source_api_version, target_info, fstab=source_info["fstab"])
1494
1495 if target_info.oem_props or source_info.oem_props:
1496 if not OPTIONS.oem_no_mount:
1497 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001498
Tao Baodf3a48b2018-01-10 16:30:43 -08001499 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001500
Tao Bao491d7e22018-02-21 13:17:22 -08001501 if not OPTIONS.no_signing:
1502 staging_file = common.MakeTempFile(suffix='.zip')
1503 else:
1504 staging_file = output_file
1505
1506 output_zip = zipfile.ZipFile(
1507 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1508
Geremy Condra36bd3652014-02-06 19:45:10 -08001509 device_specific = common.DeviceSpecificParams(
1510 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001511 source_version=source_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001512 source_tmp=OPTIONS.source_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001513 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001514 target_version=target_api_version,
Yifan Hong8a66a712019-04-04 15:37:57 -07001515 target_tmp=OPTIONS.target_tmp,
Geremy Condra36bd3652014-02-06 19:45:10 -08001516 output_zip=output_zip,
1517 script=script,
1518 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001519 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001520
Geremy Condra36bd3652014-02-06 19:45:10 -08001521 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001522 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001523 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001524 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001525 updating_boot = (not OPTIONS.two_step and
1526 (source_boot.data != target_boot.data))
1527
Geremy Condra36bd3652014-02-06 19:45:10 -08001528 target_recovery = common.GetBootableImage(
1529 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001530
Yifan Hong8a66a712019-04-04 15:37:57 -07001531 # See notes in common.GetUserImage()
Tao Baoe709b092018-02-07 12:40:00 -08001532 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1533 target_info.get('ext4_share_dup_blocks') == "true")
Yifan Hong8a66a712019-04-04 15:37:57 -07001534 system_src = common.GetUserImage("system", OPTIONS.source_tmp, source_zip,
1535 info_dict=source_info,
1536 allow_shared_blocks=allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001537
1538 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1539 "system", 4096, target_info)
Yifan Hong8a66a712019-04-04 15:37:57 -07001540 system_tgt = common.GetUserImage("system", OPTIONS.target_tmp, target_zip,
1541 info_dict=target_info,
1542 allow_shared_blocks=allow_shared_blocks,
1543 hashtree_info_generator=
1544 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001545
Tao Bao0582cb62017-12-21 11:47:01 -08001546 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001547 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001548 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001549
Tao Baof8acad12016-07-07 09:09:58 -07001550 # Check the first block of the source system partition for remount R/W only
1551 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001552 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001553 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001554 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1555 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1556 # b) the blocks listed in block map may not contain all the bytes for a given
1557 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001558 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001559 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1560 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001561 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001562 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001563 version=blockimgdiff_version,
1564 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001565
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001566 if HasVendorPartition(target_zip):
1567 if not HasVendorPartition(source_zip):
1568 raise RuntimeError("can't generate incremental that adds /vendor")
Yifan Hong8a66a712019-04-04 15:37:57 -07001569 vendor_src = common.GetUserImage("vendor", OPTIONS.source_tmp, source_zip,
1570 info_dict=source_info,
1571 allow_shared_blocks=allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001572 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1573 "vendor", 4096, target_info)
Yifan Hong8a66a712019-04-04 15:37:57 -07001574 vendor_tgt = common.GetUserImage(
1575 "vendor", OPTIONS.target_tmp, target_zip,
1576 info_dict=target_info,
1577 allow_shared_blocks=allow_shared_blocks,
1578 hashtree_info_generator=hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001579
1580 # Check first block of vendor partition for remount R/W only if
1581 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001582 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001583 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001584 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001585 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001586 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001587 version=blockimgdiff_version,
1588 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001589 else:
1590 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001591
Tao Baobcd1d162017-08-26 13:10:26 -07001592 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001593 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001594
Tao Bao481bab82017-12-21 11:23:09 -08001595 # Assertions (e.g. device properties check).
1596 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001597 device_specific.IncrementalOTA_Assertions()
1598
1599 # Two-step incremental package strategy (in chronological order,
1600 # which is *not* the order in which the generated script has
1601 # things):
1602 #
1603 # if stage is not "2/3" or "3/3":
1604 # do verification on current system
1605 # write recovery image to boot partition
1606 # set stage to "2/3"
1607 # reboot to boot partition and restart recovery
1608 # else if stage is "2/3":
1609 # write recovery image to recovery partition
1610 # set stage to "3/3"
1611 # reboot to recovery partition and restart recovery
1612 # else:
1613 # (stage must be "3/3")
1614 # perform update:
1615 # patch system files, etc.
1616 # force full install of new boot image
1617 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001618 # complete script normally
1619 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001620
1621 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001622 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001623 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001624 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001625 assert fs.fs_type.upper() == "EMMC", \
1626 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001627 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001628 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1629 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001630if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001631""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001632
1633 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1634 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001635 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001636 script.WriteRawImage("/recovery", "recovery.img")
1637 script.AppendExtra("""
1638set_stage("%(bcb_dev)s", "3/3");
1639reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001640else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001641""" % bcb_dev)
1642
Tao Baod42e97e2016-11-30 12:11:57 -08001643 # Stage 1/3: (a) Verify the current system.
1644 script.Comment("Stage 1/3")
1645
Tao Bao6c55a8a2015-04-08 15:30:27 -07001646 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001647 script.Print("Source: {}".format(source_info.fingerprint))
1648 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001649
Geremy Condra36bd3652014-02-06 19:45:10 -08001650 script.Print("Verifying current system...")
1651
1652 device_specific.IncrementalOTA_VerifyBegin()
1653
Tao Bao481bab82017-12-21 11:23:09 -08001654 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001655
Tao Baod8d14be2016-02-04 14:26:02 -08001656 # Check the required cache size (i.e. stashed blocks).
1657 size = []
1658 if system_diff:
1659 size.append(system_diff.required_cache)
1660 if vendor_diff:
1661 size.append(vendor_diff.required_cache)
1662
Geremy Condra36bd3652014-02-06 19:45:10 -08001663 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001664 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001665 d = common.Difference(target_boot, source_boot)
1666 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001667 if d is None:
1668 include_full_boot = True
1669 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1670 else:
1671 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001672
Tao Bao32fcdab2018-10-12 10:30:39 -07001673 logger.info(
1674 "boot target: %d source: %d diff: %d", target_boot.size,
1675 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001676
Tao Bao51216552018-08-26 11:53:15 -07001677 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001678
Tao Bao51216552018-08-26 11:53:15 -07001679 script.PatchPartitionCheck(
1680 "{}:{}:{}:{}".format(
1681 boot_type, boot_device, target_boot.size, target_boot.sha1),
1682 "{}:{}:{}:{}".format(
1683 boot_type, boot_device, source_boot.size, source_boot.sha1))
1684
Tao Baod8d14be2016-02-04 14:26:02 -08001685 size.append(target_boot.size)
1686
1687 if size:
1688 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001689
1690 device_specific.IncrementalOTA_VerifyEnd()
1691
1692 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001693 # Stage 1/3: (b) Write recovery image to /boot.
1694 _WriteRecoveryImageToBoot(script, output_zip)
1695
Geremy Condra36bd3652014-02-06 19:45:10 -08001696 script.AppendExtra("""
1697set_stage("%(bcb_dev)s", "2/3");
1698reboot_now("%(bcb_dev)s", "");
1699else
1700""" % bcb_dev)
1701
Tao Baod42e97e2016-11-30 12:11:57 -08001702 # Stage 3/3: Make changes.
1703 script.Comment("Stage 3/3")
1704
Jesse Zhao75bcea02015-01-06 10:59:53 -08001705 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001706 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001707 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001708 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001709 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1710 if device_specific_diffs:
1711 assert all(isinstance(diff, common.BlockDifference)
1712 for diff in device_specific_diffs), \
1713 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1714 "BlockDifference objects"
1715 for diff in device_specific_diffs:
1716 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001717
Geremy Condra36bd3652014-02-06 19:45:10 -08001718 script.Comment("---- start making changes here ----")
1719
1720 device_specific.IncrementalOTA_InstallBegin()
1721
Yifan Hong10c530d2018-12-27 17:34:18 -08001722 block_diffs = [system_diff]
1723 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001724 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001725 block_diffs.append(vendor_diff)
1726 progress_dict["vendor"] = 0.1
1727 if device_specific_diffs:
1728 block_diffs += device_specific_diffs
1729
1730 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1731 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1732 raise RuntimeError(
1733 "can't generate incremental that disables dynamic partitions")
1734 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1735 info_dict=OPTIONS.target_info_dict,
1736 source_info_dict=OPTIONS.source_info_dict,
1737 block_diffs=block_diffs,
1738 progress_dict=progress_dict)
1739 dynamic_partitions_diff.WriteScript(
1740 script, output_zip, write_verify_script=OPTIONS.verify)
1741 else:
1742 for block_diff in block_diffs:
1743 block_diff.WriteScript(script, output_zip,
1744 progress=progress_dict.get(block_diff.partition),
1745 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001746
1747 if OPTIONS.two_step:
1748 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1749 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001750 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001751
1752 if not OPTIONS.two_step:
1753 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001754 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001755 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001756 script.Print("Installing boot image...")
1757 script.WriteRawImage("/boot", "boot.img")
1758 else:
1759 # Produce the boot image by applying a patch to the current
1760 # contents of the boot partition, and write it back to the
1761 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001762 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001763 script.Print("Patching boot image...")
1764 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001765 script.PatchPartition(
1766 '{}:{}:{}:{}'.format(
1767 boot_type, boot_device, target_boot.size, target_boot.sha1),
1768 '{}:{}:{}:{}'.format(
1769 boot_type, boot_device, source_boot.size, source_boot.sha1),
1770 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001771 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001772 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001773
1774 # Do device-specific installation (eg, write radio image).
1775 device_specific.IncrementalOTA_InstallEnd()
1776
1777 if OPTIONS.extra_script is not None:
1778 script.AppendExtra(OPTIONS.extra_script)
1779
Doug Zongker922206e2014-03-04 13:16:24 -08001780 if OPTIONS.wipe_user_data:
1781 script.Print("Erasing user data...")
1782 script.FormatPartition("/data")
1783
Geremy Condra36bd3652014-02-06 19:45:10 -08001784 if OPTIONS.two_step:
1785 script.AppendExtra("""
1786set_stage("%(bcb_dev)s", "");
1787endif;
1788endif;
1789""" % bcb_dev)
1790
1791 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001792 # For downgrade OTAs, we prefer to use the update-binary in the source
1793 # build that is actually newer than the one in the target build.
1794 if OPTIONS.downgrade:
1795 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1796 else:
1797 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001798 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001799
1800 # We haven't written the metadata entry yet, which will be handled in
1801 # FinalizeMetadata().
1802 common.ZipClose(output_zip)
1803
1804 # Sign the generated zip package unless no_signing is specified.
1805 needed_property_files = (
1806 NonAbOtaPropertyFiles(),
1807 )
1808 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001809
Doug Zongker32b527d2014-03-04 10:03:02 -08001810
Tao Bao15a146a2018-02-21 16:06:59 -08001811def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001812 """Returns a target-files.zip file for generating secondary payload.
1813
1814 Although the original target-files.zip already contains secondary slot
1815 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1816 ones without _other suffix. Note that we cannot instead modify the names in
1817 META/ab_partitions.txt, because there are no matching partitions on device.
1818
1819 For the partitions that don't have secondary images, the ones for primary
1820 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1821 bootloader images in the inactive slot.
1822
1823 Args:
1824 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001825 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001826
1827 Returns:
1828 The filename of the target-files.zip for generating secondary payload.
1829 """
1830 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1831 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1832
Tao Baodba59ee2018-01-09 13:21:02 -08001833 with zipfile.ZipFile(input_file, 'r') as input_zip:
1834 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001835 namelist = input_zip.namelist()
1836
Tao Bao0ff15de2019-03-20 11:26:06 -07001837 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001838 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001839 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1840 if info.filename == 'IMAGES/system_other.img':
1841 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1842
1843 # Primary images and friends need to be skipped explicitly.
1844 elif info.filename in ('IMAGES/system.img',
1845 'IMAGES/system.map'):
1846 pass
1847
Tao Bao15a146a2018-02-21 16:06:59 -08001848 # Skip copying the postinstall config if requested.
1849 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1850 pass
1851
Tao Bao12489802018-07-12 14:47:38 -07001852 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001853 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1854
Tao Baof7140c02018-01-30 17:09:24 -08001855 common.ZipClose(target_zip)
1856
1857 return target_file
1858
1859
Tao Bao15a146a2018-02-21 16:06:59 -08001860def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1861 """Returns a target-files.zip that's not containing postinstall_config.txt.
1862
1863 This allows brillo_update_payload script to skip writing all the postinstall
1864 hooks in the generated payload. The input target-files.zip file will be
1865 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1866 contain the postinstall_config.txt entry, the input file will be returned.
1867
1868 Args:
1869 input_file: The input target-files.zip filename.
1870
1871 Returns:
1872 The filename of target-files.zip that doesn't contain postinstall config.
1873 """
1874 # We should only make a copy if postinstall_config entry exists.
1875 with zipfile.ZipFile(input_file, 'r') as input_zip:
1876 if POSTINSTALL_CONFIG not in input_zip.namelist():
1877 return input_file
1878
1879 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1880 shutil.copyfile(input_file, target_file)
1881 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1882 return target_file
1883
1884
Yifan Hong50e79542018-11-08 17:44:12 -08001885def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001886 super_block_devices,
1887 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001888 """Returns a target-files.zip for retrofitting dynamic partitions.
1889
1890 This allows brillo_update_payload to generate an OTA based on the exact
1891 bits on the block devices. Postinstall is disabled.
1892
1893 Args:
1894 input_file: The input target-files.zip filename.
1895 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001896 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001897
1898 Returns:
1899 The filename of target-files.zip with *.img replaced with super_*.img for
1900 each block device in super_block_devices.
1901 """
1902 assert super_block_devices, "No super_block_devices are specified."
1903
1904 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001905 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001906
1907 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1908 shutil.copyfile(input_file, target_file)
1909
1910 with zipfile.ZipFile(input_file, 'r') as input_zip:
1911 namelist = input_zip.namelist()
1912
Yifan Hongb433eba2019-03-06 12:42:53 -08001913 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1914
1915 # Remove partitions from META/ab_partitions.txt that is in
1916 # dynamic_partition_list but not in super_block_devices so that
1917 # brillo_update_payload won't generate update for those logical partitions.
1918 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1919 with open(ab_partitions_file) as f:
1920 ab_partitions_lines = f.readlines()
1921 ab_partitions = [line.strip() for line in ab_partitions_lines]
1922 # Assert that all super_block_devices are in ab_partitions
1923 super_device_not_updated = [partition for partition in super_block_devices
1924 if partition not in ab_partitions]
1925 assert not super_device_not_updated, \
1926 "{} is in super_block_devices but not in {}".format(
1927 super_device_not_updated, AB_PARTITIONS)
1928 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1929 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1930 with open(new_ab_partitions, 'w') as f:
1931 for partition in ab_partitions:
1932 if (partition in dynamic_partition_list and
1933 partition not in super_block_devices):
1934 logger.info("Dropping %s from ab_partitions.txt", partition)
1935 continue
1936 f.write(partition + "\n")
1937 to_delete = [AB_PARTITIONS]
1938
Yifan Hong50e79542018-11-08 17:44:12 -08001939 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001940 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001941
1942 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1943 # is a regular update on devices without dynamic partitions support.
1944 to_delete += [DYNAMIC_PARTITION_INFO]
1945
Tao Bao03fecb62018-11-28 10:59:23 -08001946 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001947 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001948 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001949
1950 common.ZipDelete(target_file, to_delete)
1951
Yifan Hong50e79542018-11-08 17:44:12 -08001952 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1953
1954 # Write super_{foo}.img as {foo}.img.
1955 for src, dst in replace.items():
1956 assert src in namelist, \
1957 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1958 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1959 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1960
Yifan Hongb433eba2019-03-06 12:42:53 -08001961 # Write new ab_partitions.txt file
1962 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1963
Yifan Hong50e79542018-11-08 17:44:12 -08001964 common.ZipClose(target_zip)
1965
1966 return target_file
1967
1968
Tao Baoc098e9e2016-01-07 13:03:56 -08001969def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1970 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001971 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001972 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001973 if not OPTIONS.no_signing:
1974 staging_file = common.MakeTempFile(suffix='.zip')
1975 else:
1976 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001977 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001978 compression=zipfile.ZIP_DEFLATED)
1979
Tao Bao481bab82017-12-21 11:23:09 -08001980 if source_file is not None:
1981 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1982 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1983 else:
1984 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1985 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001986
Tao Bao481bab82017-12-21 11:23:09 -08001987 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001988 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001989
Yifan Hong50e79542018-11-08 17:44:12 -08001990 if OPTIONS.retrofit_dynamic_partitions:
1991 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001992 target_file, target_info.get("super_block_devices").strip().split(),
1993 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001994 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001995 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1996
Tao Bao40b18822018-01-30 18:19:04 -08001997 # Generate payload.
1998 payload = Payload()
1999
2000 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07002001 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08002002 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07002003 else:
2004 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08002005 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08002006
Tao Bao40b18822018-01-30 18:19:04 -08002007 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08002008
Tao Bao40b18822018-01-30 18:19:04 -08002009 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08002010 payload_signer = PayloadSigner()
2011 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08002012
Tao Bao40b18822018-01-30 18:19:04 -08002013 # Write the payload into output zip.
2014 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002015
Tao Baof7140c02018-01-30 17:09:24 -08002016 # Generate and include the secondary payload that installs secondary images
2017 # (e.g. system_other.img).
2018 if OPTIONS.include_secondary:
2019 # We always include a full payload for the secondary slot, even when
2020 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08002021 secondary_target_file = GetTargetFilesZipForSecondaryImages(
2022 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08002023 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08002024 secondary_payload.Generate(secondary_target_file,
2025 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08002026 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08002027 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08002028
Tianjie Xucfa86222016-03-07 16:31:19 -08002029 # If dm-verity is supported for the device, copy contents of care_map
2030 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07002031 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08002032 if (target_info.get("verity") == "true" or
2033 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002034 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
2035 "META/" + x in target_zip.namelist()]
2036
2037 # Adds care_map if either the protobuf format or the plain text one exists.
2038 if care_map_list:
2039 care_map_name = care_map_list[0]
2040 care_map_data = target_zip.read("META/" + care_map_name)
2041 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08002042 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002043 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002044 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002045 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002046 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002047
Tao Baobcd1d162017-08-26 13:10:26 -07002048 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08002049 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07002050
Tao Bao21803d32017-04-19 10:16:09 -07002051 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002052
Tao Baofe5b69a2018-03-02 09:47:43 -08002053 # We haven't written the metadata entry yet, which will be handled in
2054 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002055 common.ZipClose(output_zip)
2056
Tao Bao85f16982018-03-08 16:28:33 -08002057 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2058 # all the info of the latter. However, system updaters and OTA servers need to
2059 # take time to switch to the new flag. We keep both of the flags for
2060 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002061 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002062 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002063 StreamingPropertyFiles(),
2064 )
2065 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002066
Tao Baoc098e9e2016-01-07 13:03:56 -08002067
Doug Zongkereef39442009-04-02 12:14:19 -07002068def main(argv):
2069
2070 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002071 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002072 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002073 elif o in ("-i", "--incremental_from"):
2074 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002075 elif o == "--full_radio":
2076 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002077 elif o == "--full_bootloader":
2078 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002079 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002080 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002081 elif o == "--downgrade":
2082 OPTIONS.downgrade = True
2083 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002084 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002085 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002086 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002087 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002088 elif o == "--oem_no_mount":
2089 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002090 elif o in ("-e", "--extra_script"):
2091 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002092 elif o in ("-t", "--worker_threads"):
2093 if a.isdigit():
2094 OPTIONS.worker_threads = int(a)
2095 else:
2096 raise ValueError("Cannot parse value %r for option %r - only "
2097 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002098 elif o in ("-2", "--two_step"):
2099 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002100 elif o == "--include_secondary":
2101 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002102 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002103 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002104 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002105 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002106 elif o == "--block":
2107 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002108 elif o in ("-b", "--binary"):
2109 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002110 elif o == "--stash_threshold":
2111 try:
2112 OPTIONS.stash_threshold = float(a)
2113 except ValueError:
2114 raise ValueError("Cannot parse value %r for option %r - expecting "
2115 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002116 elif o == "--log_diff":
2117 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002118 elif o == "--payload_signer":
2119 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002120 elif o == "--payload_signer_args":
2121 OPTIONS.payload_signer_args = shlex.split(a)
xunchang376cc7c2019-04-08 23:04:58 -07002122 elif o == "--payload_signer_key_size":
2123 OPTIONS.payload_signer_key_size = a
Dan Willemsencea5cd22017-03-21 14:44:27 -07002124 elif o == "--extracted_input_target_files":
2125 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002126 elif o == "--skip_postinstall":
2127 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002128 elif o == "--retrofit_dynamic_partitions":
2129 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002130 elif o == "--skip_compatibility_check":
2131 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002132 elif o == "--output_metadata_path":
2133 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002134 else:
2135 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002136 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002137
2138 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002139 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002140 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002141 "package_key=",
2142 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002143 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002144 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002145 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002146 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002147 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002148 "extra_script=",
2149 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002150 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002151 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002152 "no_signing",
2153 "block",
2154 "binary=",
2155 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002156 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002157 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002158 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002159 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002160 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002161 "payload_signer_args=",
xunchang376cc7c2019-04-08 23:04:58 -07002162 "payload_signer_key_size=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002163 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002164 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002165 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002166 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002167 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002168 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002169
2170 if len(args) != 2:
2171 common.Usage(__doc__)
2172 sys.exit(1)
2173
Tao Bao32fcdab2018-10-12 10:30:39 -07002174 common.InitLogging()
2175
Tao Bao5d182562016-02-23 11:38:39 -08002176 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002177 # We should only allow downgrading incrementals (as opposed to full).
2178 # Otherwise the device may go back from arbitrary build with this full
2179 # OTA package.
2180 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002181 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002182
Tao Bao2db13852018-01-08 22:28:57 -08002183 # Load the build info dicts from the zip directly or the extracted input
2184 # directory. We don't need to unzip the entire target-files zips, because they
2185 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2186 # When loading the info dicts, we don't need to provide the second parameter
2187 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2188 # some properties with their actual paths, such as 'selinux_fc',
2189 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002190 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002191 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002192 else:
Tao Bao2db13852018-01-08 22:28:57 -08002193 with zipfile.ZipFile(args[0], 'r') as input_zip:
2194 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002195
Tao Bao32fcdab2018-10-12 10:30:39 -07002196 logger.info("--- target info ---")
2197 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002198
2199 # Load the source build dict if applicable.
2200 if OPTIONS.incremental_source is not None:
2201 OPTIONS.target_info_dict = OPTIONS.info_dict
2202 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2203 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2204
Tao Bao32fcdab2018-10-12 10:30:39 -07002205 logger.info("--- source info ---")
2206 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002207
2208 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002209 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2210
Yifan Hong50e79542018-11-08 17:44:12 -08002211 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002212 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002213 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002214 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2215 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002216 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2217 raise common.ExternalError(
2218 "Expect to generate incremental OTA for retrofitting dynamic "
2219 "partitions, but dynamic_partition_retrofit is not set in target "
2220 "build.")
2221 logger.info("Implicitly generating retrofit incremental OTA.")
2222 OPTIONS.retrofit_dynamic_partitions = True
2223
2224 # Skip postinstall for retrofitting dynamic partitions.
2225 if OPTIONS.retrofit_dynamic_partitions:
2226 OPTIONS.skip_postinstall = True
2227
Tao Baoc098e9e2016-01-07 13:03:56 -08002228 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2229
Christian Oderf63e2cd2017-05-01 22:30:15 +02002230 # Use the default key to sign the package if not specified with package_key.
2231 # package_keys are needed on ab_updates, so always define them if an
2232 # ab_update is getting created.
2233 if not OPTIONS.no_signing or ab_update:
2234 if OPTIONS.package_key is None:
2235 OPTIONS.package_key = OPTIONS.info_dict.get(
2236 "default_system_dev_certificate",
Dan Willemsen0ab1be62019-04-09 21:35:37 -07002237 "build/make/target/product/security/testkey")
Christian Oderf63e2cd2017-05-01 22:30:15 +02002238 # Get signing keys
2239 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2240
Tao Baoc098e9e2016-01-07 13:03:56 -08002241 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002242 WriteABOTAPackageWithBrilloScript(
2243 target_file=args[0],
2244 output_file=args[1],
2245 source_file=OPTIONS.incremental_source)
2246
Tao Bao32fcdab2018-10-12 10:30:39 -07002247 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002248 return
2249
Tao Bao2db13852018-01-08 22:28:57 -08002250 # Sanity check the loaded info dicts first.
2251 if OPTIONS.info_dict.get("no_recovery") == "true":
2252 raise common.ExternalError(
2253 "--- target build has specified no recovery ---")
2254
2255 # Non-A/B OTAs rely on /cache partition to store temporary files.
2256 cache_size = OPTIONS.info_dict.get("cache_size")
2257 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002258 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002259 OPTIONS.cache_size = cache_size
2260
Doug Zongker1c390a22009-05-14 19:06:36 -07002261 if OPTIONS.extra_script is not None:
2262 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2263
Dan Willemsencea5cd22017-03-21 14:44:27 -07002264 if OPTIONS.extracted_input is not None:
2265 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002266 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002267 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002268 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002269 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002270
Tao Bao2db13852018-01-08 22:28:57 -08002271 # If the caller explicitly specified the device-specific extensions path via
2272 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2273 # is present in the target target_files. Otherwise, take the path of the file
2274 # from 'tool_extensions' in the info dict and look for that in the local
2275 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002276 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002277 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2278 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002279 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002280 OPTIONS.device_specific = from_input
2281 else:
Tao Bao2db13852018-01-08 22:28:57 -08002282 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002283
Doug Zongker37974732010-09-16 17:44:38 -07002284 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002285 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002286
Tao Bao767e3ac2015-11-10 12:19:19 -08002287 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002288 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002289 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002290 WriteFullOTAPackage(
2291 input_zip,
2292 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002293
Tao Bao32b80dc2018-01-08 22:50:47 -08002294 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002295 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002296 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002297 OPTIONS.source_tmp = common.UnzipTemp(
2298 OPTIONS.incremental_source, UNZIP_PATTERN)
2299 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2300 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002301 WriteBlockIncrementalOTAPackage(
2302 input_zip,
2303 source_zip,
2304 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002305
2306 if OPTIONS.log_diff:
2307 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002308 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002309 target_files_diff.recursiveDiff(
2310 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002311
Tao Bao32fcdab2018-10-12 10:30:39 -07002312 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002313
2314
2315if __name__ == '__main__':
2316 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002317 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002318 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002319 except common.ExternalError:
2320 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002321 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002322 finally:
2323 common.Cleanup()