blob: 37f4e38d122e42c602bdf93ec6e1ddc17f86082f [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
41 META/misc_info.txt, or "build/target/product/security/testkey" if that
42 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
171 --skip_postinstall
172 Skip the postinstall hooks when generating an A/B OTA package (default:
173 False). Note that this discards ALL the hooks, including non-optional
174 ones. Should only be used if caller knows it's safe to do so (e.g. all the
175 postinstall work is to dexopt apps and a data wipe will happen immediately
176 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700177"""
178
Tao Bao89fbb0f2017-01-10 10:47:58 -0800179from __future__ import print_function
180
Tao Bao32fcdab2018-10-12 10:30:39 -0700181import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700182import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800183import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700184import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800185import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800186import struct
Tao Bao481bab82017-12-21 11:23:09 -0800187import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700188import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700189import zipfile
190
191import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700192import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700193import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700194
Tao Bao481bab82017-12-21 11:23:09 -0800195if sys.hexversion < 0x02070000:
196 print("Python 2.7 or newer is required.", file=sys.stderr)
197 sys.exit(1)
198
Tao Bao32fcdab2018-10-12 10:30:39 -0700199logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800200
Doug Zongkereef39442009-04-02 12:14:19 -0700201OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700202OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700203OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700204OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700205OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700206OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800207OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700208OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700209OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
210if OPTIONS.worker_threads == 0:
211 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800212OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800213OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900214OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800215OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800216OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700217OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800218OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700219OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700220OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700221# Stash size cannot exceed cache_size * threshold.
222OPTIONS.cache_size = None
223OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800224OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700225OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700226OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700227OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200228OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800229OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800230OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800231OPTIONS.skip_compatibility_check = False
xunchang1cfe2512019-02-19 14:14:48 -0800232OPTIONS.output_metadata_path = None
Tao Bao15a146a2018-02-21 16:06:59 -0800233
Tao Bao8dcf7382015-05-21 14:09:49 -0700234
Tao Bao2dd1c482017-02-03 16:49:39 -0800235METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800236POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800237DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Yifan Hongb433eba2019-03-06 12:42:53 -0800238AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao0ff15de2019-03-20 11:26:06 -0700239UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'RADIO/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800240RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao6b0b2f92017-03-05 11:38:11 -0800241
Tao Bao2dd1c482017-02-03 16:49:39 -0800242
Tao Bao481bab82017-12-21 11:23:09 -0800243class BuildInfo(object):
244 """A class that holds the information for a given build.
245
246 This class wraps up the property querying for a given source or target build.
247 It abstracts away the logic of handling OEM-specific properties, and caches
248 the commonly used properties such as fingerprint.
249
250 There are two types of info dicts: a) build-time info dict, which is generated
251 at build time (i.e. included in a target_files zip); b) OEM info dict that is
252 specified at package generation time (via command line argument
253 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
254 having "oem_fingerprint_properties" in build-time info dict), all the queries
255 would be answered based on build-time info dict only. Otherwise if using
256 OEM-specific properties, some of them will be calculated from two info dicts.
257
258 Users can query properties similarly as using a dict() (e.g. info['fstab']),
259 or to query build properties via GetBuildProp() or GetVendorBuildProp().
260
261 Attributes:
262 info_dict: The build-time info dict.
263 is_ab: Whether it's a build that uses A/B OTA.
264 oem_dicts: A list of OEM dicts.
265 oem_props: A list of OEM properties that should be read from OEM dicts; None
266 if the build doesn't use any OEM-specific property.
267 fingerprint: The fingerprint of the build, which would be calculated based
268 on OEM properties if applicable.
269 device: The device name, which could come from OEM dicts if applicable.
270 """
271
Steven Laver9e73e822019-01-29 20:20:08 -0800272 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
273 "ro.product.manufacturer", "ro.product.model",
274 "ro.product.name"]
275 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "product_services",
276 "odm", "vendor", "system"]
277
Tao Bao481bab82017-12-21 11:23:09 -0800278 def __init__(self, info_dict, oem_dicts):
279 """Initializes a BuildInfo instance with the given dicts.
280
Tao Bao667c7532018-07-06 10:13:59 -0700281 Note that it only wraps up the given dicts, without making copies.
282
Tao Bao481bab82017-12-21 11:23:09 -0800283 Arguments:
284 info_dict: The build-time info dict.
285 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
286 that it always uses the first dict to calculate the fingerprint or the
287 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700288 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800289 """
290 self.info_dict = info_dict
291 self.oem_dicts = oem_dicts
292
293 self._is_ab = info_dict.get("ab_update") == "true"
294 self._oem_props = info_dict.get("oem_fingerprint_properties")
295
296 if self._oem_props:
297 assert oem_dicts, "OEM source required for this build"
298
299 # These two should be computed only after setting self._oem_props.
300 self._device = self.GetOemProperty("ro.product.device")
301 self._fingerprint = self.CalculateFingerprint()
302
303 @property
304 def is_ab(self):
305 return self._is_ab
306
307 @property
308 def device(self):
309 return self._device
310
311 @property
312 def fingerprint(self):
313 return self._fingerprint
314
315 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700316 def vendor_fingerprint(self):
317 if "vendor.build.prop" not in self.info_dict:
318 return None
319 vendor_build_prop = self.info_dict["vendor.build.prop"]
320 if "ro.vendor.build.fingerprint" in vendor_build_prop:
321 return vendor_build_prop["ro.vendor.build.fingerprint"]
322 if "ro.vendor.build.thumbprint" in vendor_build_prop:
323 return vendor_build_prop["ro.vendor.build.thumbprint"]
324 return None
325
326 @property
Tao Bao481bab82017-12-21 11:23:09 -0800327 def oem_props(self):
328 return self._oem_props
329
330 def __getitem__(self, key):
331 return self.info_dict[key]
332
Tao Bao667c7532018-07-06 10:13:59 -0700333 def __setitem__(self, key, value):
334 self.info_dict[key] = value
335
Tao Bao481bab82017-12-21 11:23:09 -0800336 def get(self, key, default=None):
337 return self.info_dict.get(key, default)
338
Tao Bao667c7532018-07-06 10:13:59 -0700339 def items(self):
340 return self.info_dict.items()
341
Tao Bao481bab82017-12-21 11:23:09 -0800342 def GetBuildProp(self, prop):
343 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800344 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
345 return self._ResolveRoProductBuildProp(prop)
346
Tao Bao481bab82017-12-21 11:23:09 -0800347 try:
348 return self.info_dict.get("build.prop", {})[prop]
349 except KeyError:
350 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
351
Steven Laver9e73e822019-01-29 20:20:08 -0800352 def _ResolveRoProductBuildProp(self, prop):
353 """Resolves the inquired ro.product.* build property"""
354 prop_val = self.info_dict.get("build.prop", {}).get(prop)
355 if prop_val:
356 return prop_val
357
358 source_order_val = self.info_dict.get("build.prop", {}).get(
359 "ro.product.property_source_order")
360 if source_order_val:
361 source_order = source_order_val.split(",")
362 else:
363 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
364
365 # Check that all sources in ro.product.property_source_order are valid
366 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
367 for x in source_order]):
368 raise common.ExternalError(
369 "Invalid ro.product.property_source_order '{}'".format(source_order))
370
371 for source in source_order:
372 source_prop = prop.replace("ro.product", "ro.product.{}".format(source),
373 1)
374 prop_val = self.info_dict.get("{}.build.prop".format(source), {}).get(
375 source_prop)
376 if prop_val:
377 return prop_val
378
379 raise common.ExternalError("couldn't resolve {}".format(prop))
380
Tao Bao481bab82017-12-21 11:23:09 -0800381 def GetVendorBuildProp(self, prop):
382 """Returns the inquired vendor build property."""
383 try:
384 return self.info_dict.get("vendor.build.prop", {})[prop]
385 except KeyError:
386 raise common.ExternalError(
387 "couldn't find %s in vendor.build.prop" % (prop,))
388
389 def GetOemProperty(self, key):
390 if self.oem_props is not None and key in self.oem_props:
391 return self.oem_dicts[0][key]
392 return self.GetBuildProp(key)
393
394 def CalculateFingerprint(self):
395 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800396 try:
397 return self.GetBuildProp("ro.build.fingerprint")
398 except common.ExternalError:
399 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
400 self.GetBuildProp("ro.product.brand"),
401 self.GetBuildProp("ro.product.name"),
402 self.GetBuildProp("ro.product.device"),
403 self.GetBuildProp("ro.build.version.release"),
404 self.GetBuildProp("ro.build.id"),
405 self.GetBuildProp("ro.build.version.incremental"),
406 self.GetBuildProp("ro.build.type"),
407 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800408 return "%s/%s/%s:%s" % (
409 self.GetOemProperty("ro.product.brand"),
410 self.GetOemProperty("ro.product.name"),
411 self.GetOemProperty("ro.product.device"),
412 self.GetBuildProp("ro.build.thumbprint"))
413
414 def WriteMountOemScript(self, script):
415 assert self.oem_props is not None
416 recovery_mount_options = self.info_dict.get("recovery_mount_options")
417 script.Mount("/oem", recovery_mount_options)
418
419 def WriteDeviceAssertions(self, script, oem_no_mount):
420 # Read the property directly if not using OEM properties.
421 if not self.oem_props:
422 script.AssertDevice(self.device)
423 return
424
425 # Otherwise assert OEM properties.
426 if not self.oem_dicts:
427 raise common.ExternalError(
428 "No OEM file provided to answer expected assertions")
429
430 for prop in self.oem_props.split():
431 values = []
432 for oem_dict in self.oem_dicts:
433 if prop in oem_dict:
434 values.append(oem_dict[prop])
435 if not values:
436 raise common.ExternalError(
437 "The OEM file is missing the property %s" % (prop,))
438 script.AssertOemProperty(prop, values, oem_no_mount)
439
440
Tao Baofabe0832018-01-17 15:52:28 -0800441class PayloadSigner(object):
442 """A class that wraps the payload signing works.
443
444 When generating a Payload, hashes of the payload and metadata files will be
445 signed with the device key, either by calling an external payload signer or
446 by calling openssl with the package key. This class provides a unified
447 interface, so that callers can just call PayloadSigner.Sign().
448
449 If an external payload signer has been specified (OPTIONS.payload_signer), it
450 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
451 that the signing key should be provided as part of the payload_signer_args.
452 Otherwise without an external signer, it uses the package key
453 (OPTIONS.package_key) and calls openssl for the signing works.
454 """
455
456 def __init__(self):
457 if OPTIONS.payload_signer is None:
458 # Prepare the payload signing key.
459 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
460 pw = OPTIONS.key_passwords[OPTIONS.package_key]
461
462 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
463 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
464 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
465 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700466 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800467
468 self.signer = "openssl"
469 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
470 "-pkeyopt", "digest:sha256"]
471 else:
472 self.signer = OPTIONS.payload_signer
473 self.signer_args = OPTIONS.payload_signer_args
474
475 def Sign(self, in_file):
476 """Signs the given input file. Returns the output filename."""
477 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
478 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700479 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800480 return out_file
481
482
Tao Bao40b18822018-01-30 18:19:04 -0800483class Payload(object):
484 """Manages the creation and the signing of an A/B OTA Payload."""
485
486 PAYLOAD_BIN = 'payload.bin'
487 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800488 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
489 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800490
Tao Bao667ff572018-02-10 00:02:40 -0800491 def __init__(self, secondary=False):
492 """Initializes a Payload instance.
493
494 Args:
495 secondary: Whether it's generating a secondary payload (default: False).
496 """
Tao Bao40b18822018-01-30 18:19:04 -0800497 self.payload_file = None
498 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800499 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800500
501 def Generate(self, target_file, source_file=None, additional_args=None):
502 """Generates a payload from the given target-files zip(s).
503
504 Args:
505 target_file: The filename of the target build target-files zip.
506 source_file: The filename of the source build target-files zip; or None if
507 generating a full OTA.
508 additional_args: A list of additional args that should be passed to
509 brillo_update_payload script; or None.
510 """
511 if additional_args is None:
512 additional_args = []
513
514 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
515 cmd = ["brillo_update_payload", "generate",
516 "--payload", payload_file,
517 "--target_image", target_file]
518 if source_file is not None:
519 cmd.extend(["--source_image", source_file])
520 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700521 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800522
523 self.payload_file = payload_file
524 self.payload_properties = None
525
526 def Sign(self, payload_signer):
527 """Generates and signs the hashes of the payload and metadata.
528
529 Args:
530 payload_signer: A PayloadSigner() instance that serves the signing work.
531
532 Raises:
533 AssertionError: On any failure when calling brillo_update_payload script.
534 """
535 assert isinstance(payload_signer, PayloadSigner)
536
537 # 1. Generate hashes of the payload and metadata files.
538 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
539 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
540 cmd = ["brillo_update_payload", "hash",
541 "--unsigned_payload", self.payload_file,
542 "--signature_size", "256",
543 "--metadata_hash_file", metadata_sig_file,
544 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700545 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800546
547 # 2. Sign the hashes.
548 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
549 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
550
551 # 3. Insert the signatures back into the payload file.
552 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
553 suffix=".bin")
554 cmd = ["brillo_update_payload", "sign",
555 "--unsigned_payload", self.payload_file,
556 "--payload", signed_payload_file,
557 "--signature_size", "256",
558 "--metadata_signature_file", signed_metadata_sig_file,
559 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700560 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800561
562 # 4. Dump the signed payload properties.
563 properties_file = common.MakeTempFile(prefix="payload-properties-",
564 suffix=".txt")
565 cmd = ["brillo_update_payload", "properties",
566 "--payload", signed_payload_file,
567 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700568 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800569
Tao Bao667ff572018-02-10 00:02:40 -0800570 if self.secondary:
571 with open(properties_file, "a") as f:
572 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
573
Tao Bao40b18822018-01-30 18:19:04 -0800574 if OPTIONS.wipe_user_data:
575 with open(properties_file, "a") as f:
576 f.write("POWERWASH=1\n")
577
578 self.payload_file = signed_payload_file
579 self.payload_properties = properties_file
580
Tao Bao667ff572018-02-10 00:02:40 -0800581 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800582 """Writes the payload to the given zip.
583
584 Args:
585 output_zip: The output ZipFile instance.
586 """
587 assert self.payload_file is not None
588 assert self.payload_properties is not None
589
Tao Bao667ff572018-02-10 00:02:40 -0800590 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800591 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
592 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
593 else:
594 payload_arcname = Payload.PAYLOAD_BIN
595 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
596
Tao Bao40b18822018-01-30 18:19:04 -0800597 # Add the signed payload file and properties into the zip. In order to
598 # support streaming, we pack them as ZIP_STORED. So these entries can be
599 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800600 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800601 compress_type=zipfile.ZIP_STORED)
602 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800603 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800604 compress_type=zipfile.ZIP_STORED)
605
606
Doug Zongkereef39442009-04-02 12:14:19 -0700607def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200608 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700609
Doug Zongker951495f2009-08-14 12:44:19 -0700610 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
611 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700612
613
Tao Bao481bab82017-12-21 11:23:09 -0800614def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800615 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800616 if not oem_source:
617 return None
618
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800619 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800620 for oem_file in oem_source:
621 with open(oem_file) as fp:
622 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800623 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700624
Doug Zongkereef39442009-04-02 12:14:19 -0700625
Tao Baod42e97e2016-11-30 12:11:57 -0800626def _WriteRecoveryImageToBoot(script, output_zip):
627 """Find and write recovery image to /boot in two-step OTA.
628
629 In two-step OTAs, we write recovery image to /boot as the first step so that
630 we can reboot to there and install a new recovery image to /recovery.
631 A special "recovery-two-step.img" will be preferred, which encodes the correct
632 path of "/boot". Otherwise the device may show "device is corrupt" message
633 when booting into /boot.
634
635 Fall back to using the regular recovery.img if the two-step recovery image
636 doesn't exist. Note that rebuilding the special image at this point may be
637 infeasible, because we don't have the desired boot signer and keys when
638 calling ota_from_target_files.py.
639 """
640
641 recovery_two_step_img_name = "recovery-two-step.img"
642 recovery_two_step_img_path = os.path.join(
643 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
644 if os.path.exists(recovery_two_step_img_path):
645 recovery_two_step_img = common.GetBootableImage(
646 recovery_two_step_img_name, recovery_two_step_img_name,
647 OPTIONS.input_tmp, "RECOVERY")
648 common.ZipWriteStr(
649 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700650 logger.info(
651 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800652 script.WriteRawImage("/boot", recovery_two_step_img_name)
653 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700654 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800655 # The "recovery.img" entry has been written into package earlier.
656 script.WriteRawImage("/boot", "recovery.img")
657
658
Doug Zongkerc9253822014-02-04 12:17:58 -0800659def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700660 namelist = [name for name in target_files_zip.namelist()]
661 return ("SYSTEM/recovery-from-boot.p" in namelist or
662 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700663
Tao Bao457cbf62017-03-06 09:56:01 -0800664
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700665def HasVendorPartition(target_files_zip):
666 try:
667 target_files_zip.getinfo("VENDOR/")
668 return True
669 except KeyError:
670 return False
671
Tao Bao457cbf62017-03-06 09:56:01 -0800672
Tao Bao481bab82017-12-21 11:23:09 -0800673def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700674 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800675 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700676
677
Tao Bao481bab82017-12-21 11:23:09 -0800678def WriteFingerprintAssertion(script, target_info, source_info):
679 source_oem_props = source_info.oem_props
680 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700681
Tao Bao481bab82017-12-21 11:23:09 -0800682 if source_oem_props is None and target_oem_props is None:
683 script.AssertSomeFingerprint(
684 source_info.fingerprint, target_info.fingerprint)
685 elif source_oem_props is not None and target_oem_props is not None:
686 script.AssertSomeThumbprint(
687 target_info.GetBuildProp("ro.build.thumbprint"),
688 source_info.GetBuildProp("ro.build.thumbprint"))
689 elif source_oem_props is None and target_oem_props is not None:
690 script.AssertFingerprintOrThumbprint(
691 source_info.fingerprint,
692 target_info.GetBuildProp("ro.build.thumbprint"))
693 else:
694 script.AssertFingerprintOrThumbprint(
695 target_info.fingerprint,
696 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700697
Doug Zongkerfc44a512014-08-26 13:10:25 -0700698
Tao Bao481bab82017-12-21 11:23:09 -0800699def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
700 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700701 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700702
703 Metadata used for on-device compatibility verification is retrieved from
704 target_zip then added to compatibility.zip which is added to the output_zip
705 archive.
706
Tao Baobcd1d162017-08-26 13:10:26 -0700707 Compatibility archive should only be included for devices that have enabled
708 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700709
710 Args:
711 target_zip: Zip file containing the source files to be included for OTA.
712 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800713 target_info: The BuildInfo instance that holds the target build info.
714 source_info: The BuildInfo instance that holds the source build info, if
715 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700716 """
717
Tao Baobcd1d162017-08-26 13:10:26 -0700718 def AddCompatibilityArchive(system_updated, vendor_updated):
719 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700720
Tao Baobcd1d162017-08-26 13:10:26 -0700721 Args:
722 system_updated: If True, the system image will be updated and therefore
723 its metadata should be included.
724 vendor_updated: If True, the vendor image will be updated and therefore
725 its metadata should be included.
726 """
727 # Determine what metadata we need. Files are names relative to META/.
728 compatibility_files = []
729 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
730 system_metadata = ("system_manifest.xml", "system_matrix.xml")
731 if vendor_updated:
732 compatibility_files += vendor_metadata
733 if system_updated:
734 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700735
Tao Baobcd1d162017-08-26 13:10:26 -0700736 # Create new archive.
737 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800738 compatibility_archive_zip = zipfile.ZipFile(
739 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700740
Tao Baobcd1d162017-08-26 13:10:26 -0700741 # Add metadata.
742 for file_name in compatibility_files:
743 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700744
Tao Baobcd1d162017-08-26 13:10:26 -0700745 if target_file_name in target_zip.namelist():
746 data = target_zip.read(target_file_name)
747 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700748
Tao Baobcd1d162017-08-26 13:10:26 -0700749 # Ensure files are written before we copy into output_zip.
750 compatibility_archive_zip.close()
751
752 # Only add the archive if we have any compatibility info.
753 if compatibility_archive_zip.namelist():
754 common.ZipWrite(output_zip, compatibility_archive.name,
755 arcname="compatibility.zip",
756 compress_type=zipfile.ZIP_STORED)
757
758 # Will only proceed if the target has enabled the Treble support (as well as
759 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800760 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700761 return
762
xunchangabfa2652019-02-19 16:27:10 -0800763 # Skip adding the compatibility package as a workaround for b/114240221. The
764 # compatibility will always fail on devices without qualified kernels.
765 if OPTIONS.skip_compatibility_check:
766 return
767
Tao Baobcd1d162017-08-26 13:10:26 -0700768 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800769 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700770 AddCompatibilityArchive(True, True)
771 return
772
Tao Bao481bab82017-12-21 11:23:09 -0800773 source_fp = source_info.fingerprint
774 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700775 system_updated = source_fp != target_fp
776
Tao Baoea6cbd02018-09-05 13:06:37 -0700777 source_fp_vendor = source_info.vendor_fingerprint
778 target_fp_vendor = target_info.vendor_fingerprint
779 # vendor build fingerprints could be possibly blacklisted at build time. For
780 # such a case, we consider the vendor images being changed.
781 if source_fp_vendor is None or target_fp_vendor is None:
782 vendor_updated = True
783 else:
784 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700785
786 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700787
788
Tao Bao491d7e22018-02-21 13:17:22 -0800789def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800790 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700791
Tao Bao481bab82017-12-21 11:23:09 -0800792 # We don't know what version it will be installed on top of. We expect the API
793 # just won't change very often. Similarly for fstab, it might have changed in
794 # the target build.
795 target_api_version = target_info["recovery_api_version"]
796 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700797
Tao Bao481bab82017-12-21 11:23:09 -0800798 if target_info.oem_props and not OPTIONS.oem_no_mount:
799 target_info.WriteMountOemScript(script)
800
Tao Baodf3a48b2018-01-10 16:30:43 -0800801 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700802
Tao Bao491d7e22018-02-21 13:17:22 -0800803 if not OPTIONS.no_signing:
804 staging_file = common.MakeTempFile(suffix='.zip')
805 else:
806 staging_file = output_file
807
808 output_zip = zipfile.ZipFile(
809 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
810
Doug Zongker05d3dea2009-06-22 11:32:31 -0700811 device_specific = common.DeviceSpecificParams(
812 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800813 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700814 output_zip=output_zip,
815 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700816 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700817 metadata=metadata,
818 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700819
Tao Bao457cbf62017-03-06 09:56:01 -0800820 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800821
Tao Bao481bab82017-12-21 11:23:09 -0800822 # Assertions (e.g. downgrade check, device properties check).
823 ts = target_info.GetBuildProp("ro.build.date.utc")
824 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700825 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700826
Tao Bao481bab82017-12-21 11:23:09 -0800827 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700828 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800829
830 # Two-step package strategy (in chronological order, which is *not*
831 # the order in which the generated script has things):
832 #
833 # if stage is not "2/3" or "3/3":
834 # write recovery image to boot partition
835 # set stage to "2/3"
836 # reboot to boot partition and restart recovery
837 # else if stage is "2/3":
838 # write recovery image to recovery partition
839 # set stage to "3/3"
840 # reboot to recovery partition and restart recovery
841 # else:
842 # (stage must be "3/3")
843 # set stage to ""
844 # do normal full package installation:
845 # wipe and install system, boot image, etc.
846 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700847 # complete script normally
848 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800849
850 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
851 OPTIONS.input_tmp, "RECOVERY")
852 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800853 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800854 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800855 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800856 assert fs.fs_type.upper() == "EMMC", \
857 "two-step packages only supported on devices with EMMC /misc partitions"
858 bcb_dev = {"bcb_dev": fs.device}
859 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
860 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700861if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800862""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800863
864 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
865 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800866 script.WriteRawImage("/recovery", "recovery.img")
867 script.AppendExtra("""
868set_stage("%(bcb_dev)s", "3/3");
869reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700870else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800871""" % bcb_dev)
872
Tao Baod42e97e2016-11-30 12:11:57 -0800873 # Stage 3/3: Make changes.
874 script.Comment("Stage 3/3")
875
Tao Bao6c55a8a2015-04-08 15:30:27 -0700876 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800877 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700878
Doug Zongkere5ff5902012-01-17 10:55:37 -0800879 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700880
Doug Zongker01ce19c2014-02-04 13:48:15 -0800881 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700882
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700883 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800884 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700885 if HasVendorPartition(input_zip):
886 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700887
Doug Zongker4b9596f2014-06-09 14:15:45 -0700888 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800889
Tao Baoe709b092018-02-07 12:40:00 -0800890 # See the notes in WriteBlockIncrementalOTAPackage().
891 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
892
Yifan Hong10c530d2018-12-27 17:34:18 -0800893 def GetBlockDifference(partition):
894 # Full OTA is done as an "incremental" against an empty source image. This
895 # has the effect of writing new data from the package to the entire
896 # partition, but lets us reuse the updater code that writes incrementals to
897 # do it.
898 tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
899 allow_shared_blocks)
900 tgt.ResetFileMap()
901 diff = common.BlockDifference(partition, tgt, src=None)
902 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700903
Yifan Hong10c530d2018-12-27 17:34:18 -0800904 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
905 if device_specific_diffs:
906 assert all(isinstance(diff, common.BlockDifference)
907 for diff in device_specific_diffs), \
908 "FullOTA_GetBlockDifferences is not returning a list of " \
909 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800910
Yifan Hong10c530d2018-12-27 17:34:18 -0800911 progress_dict = dict()
912 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700913 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800914 block_diffs.append(GetBlockDifference("vendor"))
915 progress_dict["vendor"] = 0.1
916 if device_specific_diffs:
917 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700918
Yifan Hong10c530d2018-12-27 17:34:18 -0800919 if target_info.get('use_dynamic_partitions') == "true":
920 # Use empty source_info_dict to indicate that all partitions / groups must
921 # be re-added.
922 dynamic_partitions_diff = common.DynamicPartitionsDifference(
923 info_dict=OPTIONS.info_dict,
924 block_diffs=block_diffs,
925 progress_dict=progress_dict)
926 dynamic_partitions_diff.WriteScript(script, output_zip,
927 write_verify_script=OPTIONS.verify)
928 else:
929 for block_diff in block_diffs:
930 block_diff.WriteScript(script, output_zip,
931 progress=progress_dict.get(block_diff.partition),
932 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700933
Tao Bao481bab82017-12-21 11:23:09 -0800934 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700935
Yifan Hong10c530d2018-12-27 17:34:18 -0800936 boot_img = common.GetBootableImage(
937 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800938 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700939 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700940
Doug Zongker01ce19c2014-02-04 13:48:15 -0800941 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700942 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700943
Doug Zongker01ce19c2014-02-04 13:48:15 -0800944 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700945 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700946
Doug Zongker1c390a22009-05-14 19:06:36 -0700947 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700948 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700949
Doug Zongker14833602010-02-02 13:12:04 -0800950 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800951
Doug Zongker922206e2014-03-04 13:16:24 -0800952 if OPTIONS.wipe_user_data:
953 script.ShowProgress(0.1, 10)
954 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700955
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800956 if OPTIONS.two_step:
957 script.AppendExtra("""
958set_stage("%(bcb_dev)s", "");
959""" % bcb_dev)
960 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800961
962 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
963 script.Comment("Stage 1/3")
964 _WriteRecoveryImageToBoot(script, output_zip)
965
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800966 script.AppendExtra("""
967set_stage("%(bcb_dev)s", "2/3");
968reboot_now("%(bcb_dev)s", "");
969endif;
970endif;
971""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800972
Tao Bao5d182562016-02-23 11:38:39 -0800973 script.SetProgress(1)
974 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800975 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800976
977 # We haven't written the metadata entry, which will be done in
978 # FinalizeMetadata.
979 common.ZipClose(output_zip)
980
981 needed_property_files = (
982 NonAbOtaPropertyFiles(),
983 )
984 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700985
Doug Zongkerfc44a512014-08-26 13:10:25 -0700986
xunchang1cfe2512019-02-19 14:14:48 -0800987def WriteMetadata(metadata, output):
988 """Writes the metadata to the zip archive or a file.
989
990 Args:
991 metadata: The metadata dict for the package.
992 output: A ZipFile object or a string of the output file path.
993 """
994
Tao Bao2dd1c482017-02-03 16:49:39 -0800995 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
xunchang1cfe2512019-02-19 14:14:48 -0800996 if isinstance(output, zipfile.ZipFile):
997 common.ZipWriteStr(output, METADATA_NAME, value,
998 compress_type=zipfile.ZIP_STORED)
999 return
1000
1001 with open(output, 'w') as f:
1002 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001003
Doug Zongkerfc44a512014-08-26 13:10:25 -07001004
Tao Bao481bab82017-12-21 11:23:09 -08001005def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001006 # Only incremental OTAs are allowed to reach here.
1007 assert OPTIONS.incremental_source is not None
1008
Tao Bao481bab82017-12-21 11:23:09 -08001009 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1010 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -08001011 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1012
1013 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001014 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001015 raise RuntimeError(
1016 "--downgrade or --override_timestamp specified but no downgrade "
1017 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001018 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001019 else:
1020 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001021 raise RuntimeError(
1022 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1023 "Need to specify --override_timestamp OR --downgrade to allow "
1024 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001025
1026
Tao Baodf3a48b2018-01-10 16:30:43 -08001027def GetPackageMetadata(target_info, source_info=None):
1028 """Generates and returns the metadata dict.
1029
1030 It generates a dict() that contains the info to be written into an OTA
1031 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001032 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001033
1034 Args:
1035 target_info: The BuildInfo instance that holds the target build info.
1036 source_info: The BuildInfo instance that holds the source build info, or
1037 None if generating full OTA.
1038
1039 Returns:
1040 A dict to be written into package metadata entry.
1041 """
1042 assert isinstance(target_info, BuildInfo)
1043 assert source_info is None or isinstance(source_info, BuildInfo)
1044
1045 metadata = {
1046 'post-build' : target_info.fingerprint,
1047 'post-build-incremental' : target_info.GetBuildProp(
1048 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001049 'post-sdk-level' : target_info.GetBuildProp(
1050 'ro.build.version.sdk'),
1051 'post-security-patch-level' : target_info.GetBuildProp(
1052 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001053 }
1054
1055 if target_info.is_ab:
1056 metadata['ota-type'] = 'AB'
1057 metadata['ota-required-cache'] = '0'
1058 else:
1059 metadata['ota-type'] = 'BLOCK'
1060
1061 if OPTIONS.wipe_user_data:
1062 metadata['ota-wipe'] = 'yes'
1063
Tao Bao393eeb42019-03-06 16:00:38 -08001064 if OPTIONS.retrofit_dynamic_partitions:
1065 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1066
Tao Baodf3a48b2018-01-10 16:30:43 -08001067 is_incremental = source_info is not None
1068 if is_incremental:
1069 metadata['pre-build'] = source_info.fingerprint
1070 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1071 'ro.build.version.incremental')
1072 metadata['pre-device'] = source_info.device
1073 else:
1074 metadata['pre-device'] = target_info.device
1075
Tao Baofaa8e0b2018-04-12 14:31:43 -07001076 # Use the actual post-timestamp, even for a downgrade case.
1077 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1078
1079 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001080 if is_incremental:
1081 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001082
1083 return metadata
1084
1085
Tao Baod3fc38a2018-03-08 16:09:01 -08001086class PropertyFiles(object):
1087 """A class that computes the property-files string for an OTA package.
1088
1089 A property-files string is a comma-separated string that contains the
1090 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1091 can be fetched directly with the package URL along with the offset/size info.
1092 These strings can be used for streaming A/B OTAs, or allowing an updater to
1093 download package metadata entry directly, without paying the cost of
1094 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001095
Tao Baocc8e2662018-03-01 19:30:00 -08001096 Computing the final property-files string requires two passes. Because doing
1097 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1098 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1099 values.
1100
1101 This class provides functions to be called for each pass. The general flow is
1102 as follows.
1103
Tao Baod3fc38a2018-03-08 16:09:01 -08001104 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001105 # The first pass, which writes placeholders before doing initial signing.
1106 property_files.Compute()
1107 SignOutput()
1108
1109 # The second pass, by replacing the placeholders with actual data.
1110 property_files.Finalize()
1111 SignOutput()
1112
1113 And the caller can additionally verify the final result.
1114
1115 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001116 """
1117
Tao Baocc8e2662018-03-01 19:30:00 -08001118 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001119 self.name = None
1120 self.required = ()
1121 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001122
Tao Baocc8e2662018-03-01 19:30:00 -08001123 def Compute(self, input_zip):
1124 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001125
Tao Baocc8e2662018-03-01 19:30:00 -08001126 We reserve extra space for the offset and size of the metadata entry itself,
1127 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001128
Tao Baocc8e2662018-03-01 19:30:00 -08001129 Args:
1130 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001131
Tao Baocc8e2662018-03-01 19:30:00 -08001132 Returns:
1133 A string with placeholders for the metadata offset/size info, e.g.
1134 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1135 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001136 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001137
Tao Baod2ce2ed2018-03-16 12:59:42 -07001138 class InsufficientSpaceException(Exception):
1139 pass
1140
Tao Baocc8e2662018-03-01 19:30:00 -08001141 def Finalize(self, input_zip, reserved_length):
1142 """Finalizes a property-files string with actual METADATA offset/size info.
1143
1144 The input ZIP file has been signed, with the ZIP entries in the desired
1145 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1146 the ZIP entry offsets and construct the property-files string with actual
1147 data. Note that during this process, we must pad the property-files string
1148 to the reserved length, so that the METADATA entry size remains the same.
1149 Otherwise the entries' offsets and sizes may change again.
1150
1151 Args:
1152 input_zip: The input ZIP file.
1153 reserved_length: The reserved length of the property-files string during
1154 the call to Compute(). The final string must be no more than this
1155 size.
1156
1157 Returns:
1158 A property-files string including the metadata offset/size info, e.g.
1159 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1160
1161 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001162 InsufficientSpaceException: If the reserved length is insufficient to hold
1163 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001164 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001165 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001166 if len(result) > reserved_length:
1167 raise self.InsufficientSpaceException(
1168 'Insufficient reserved space: reserved={}, actual={}'.format(
1169 reserved_length, len(result)))
1170
Tao Baocc8e2662018-03-01 19:30:00 -08001171 result += ' ' * (reserved_length - len(result))
1172 return result
1173
1174 def Verify(self, input_zip, expected):
1175 """Verifies the input ZIP file contains the expected property-files string.
1176
1177 Args:
1178 input_zip: The input ZIP file.
1179 expected: The property-files string that's computed from Finalize().
1180
1181 Raises:
1182 AssertionError: On finding a mismatch.
1183 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001184 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001185 assert actual == expected, \
1186 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1187
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001188 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1189 """
1190 Constructs the property-files string per request.
1191
1192 Args:
1193 zip_file: The input ZIP file.
1194 reserved_length: The reserved length of the property-files string.
1195
1196 Returns:
1197 A property-files string including the metadata offset/size info, e.g.
1198 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1199 """
Tao Baocc8e2662018-03-01 19:30:00 -08001200
1201 def ComputeEntryOffsetSize(name):
1202 """Computes the zip entry offset and size."""
1203 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001204 offset = info.header_offset
1205 offset += zipfile.sizeFileHeader
1206 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001207 size = info.file_size
1208 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1209
1210 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001211 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001212 for entry in self.required:
1213 tokens.append(ComputeEntryOffsetSize(entry))
1214 for entry in self.optional:
1215 if entry in zip_file.namelist():
1216 tokens.append(ComputeEntryOffsetSize(entry))
1217
1218 # 'META-INF/com/android/metadata' is required. We don't know its actual
1219 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001220 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1221 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1222 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1223 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001224 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001225 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001226 else:
1227 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1228
1229 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001230
Tao Bao85f16982018-03-08 16:28:33 -08001231 def _GetPrecomputed(self, input_zip):
1232 """Computes the additional tokens to be included into the property-files.
1233
1234 This applies to tokens without actual ZIP entries, such as
1235 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1236 that they can download the payload metadata directly with the info.
1237
1238 Args:
1239 input_zip: The input zip file.
1240
1241 Returns:
1242 A list of strings (tokens) to be added to the property-files string.
1243 """
1244 # pylint: disable=no-self-use
1245 # pylint: disable=unused-argument
1246 return []
1247
Tao Baofe5b69a2018-03-02 09:47:43 -08001248
Tao Baod3fc38a2018-03-08 16:09:01 -08001249class StreamingPropertyFiles(PropertyFiles):
1250 """A subclass for computing the property-files for streaming A/B OTAs."""
1251
1252 def __init__(self):
1253 super(StreamingPropertyFiles, self).__init__()
1254 self.name = 'ota-streaming-property-files'
1255 self.required = (
1256 # payload.bin and payload_properties.txt must exist.
1257 'payload.bin',
1258 'payload_properties.txt',
1259 )
1260 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001261 # care_map is available only if dm-verity is enabled.
1262 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001263 'care_map.txt',
1264 # compatibility.zip is available only if target supports Treble.
1265 'compatibility.zip',
1266 )
1267
1268
Tao Bao85f16982018-03-08 16:28:33 -08001269class AbOtaPropertyFiles(StreamingPropertyFiles):
1270 """The property-files for A/B OTA that includes payload_metadata.bin info.
1271
1272 Since P, we expose one more token (aka property-file), in addition to the ones
1273 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1274 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1275 doesn't exist as a separate ZIP entry, but can be used to verify if the
1276 payload can be applied on the given device.
1277
1278 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1279 and the newly added 'ota-property-files' in P. The new token will only be
1280 available in 'ota-property-files'.
1281 """
1282
1283 def __init__(self):
1284 super(AbOtaPropertyFiles, self).__init__()
1285 self.name = 'ota-property-files'
1286
1287 def _GetPrecomputed(self, input_zip):
1288 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1289 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1290
1291 @staticmethod
1292 def _GetPayloadMetadataOffsetAndSize(input_zip):
1293 """Computes the offset and size of the payload metadata for a given package.
1294
1295 (From system/update_engine/update_metadata.proto)
1296 A delta update file contains all the deltas needed to update a system from
1297 one specific version to another specific version. The update format is
1298 represented by this struct pseudocode:
1299
1300 struct delta_update_file {
1301 char magic[4] = "CrAU";
1302 uint64 file_format_version;
1303 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1304
1305 // Only present if format_version > 1:
1306 uint32 metadata_signature_size;
1307
1308 // The Bzip2 compressed DeltaArchiveManifest
1309 char manifest[metadata_signature_size];
1310
1311 // The signature of the metadata (from the beginning of the payload up to
1312 // this location, not including the signature itself). This is a
1313 // serialized Signatures message.
1314 char medatada_signature_message[metadata_signature_size];
1315
1316 // Data blobs for files, no specific format. The specific offset
1317 // and length of each data blob is recorded in the DeltaArchiveManifest.
1318 struct {
1319 char data[];
1320 } blobs[];
1321
1322 // These two are not signed:
1323 uint64 payload_signatures_message_size;
1324 char payload_signatures_message[];
1325 };
1326
1327 'payload-metadata.bin' contains all the bytes from the beginning of the
1328 payload, till the end of 'medatada_signature_message'.
1329 """
1330 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001331 payload_offset = payload_info.header_offset
1332 payload_offset += zipfile.sizeFileHeader
1333 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001334 payload_size = payload_info.file_size
1335
1336 with input_zip.open('payload.bin', 'r') as payload_fp:
1337 header_bin = payload_fp.read(24)
1338
1339 # network byte order (big-endian)
1340 header = struct.unpack("!IQQL", header_bin)
1341
1342 # 'CrAU'
1343 magic = header[0]
1344 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1345
1346 manifest_size = header[2]
1347 metadata_signature_size = header[3]
1348 metadata_total = 24 + manifest_size + metadata_signature_size
1349 assert metadata_total < payload_size
1350
1351 return (payload_offset, metadata_total)
1352
1353
Tao Bao491d7e22018-02-21 13:17:22 -08001354class NonAbOtaPropertyFiles(PropertyFiles):
1355 """The property-files for non-A/B OTA.
1356
1357 For non-A/B OTA, the property-files string contains the info for METADATA
1358 entry, with which a system updater can be fetched the package metadata prior
1359 to downloading the entire package.
1360 """
1361
1362 def __init__(self):
1363 super(NonAbOtaPropertyFiles, self).__init__()
1364 self.name = 'ota-property-files'
1365
1366
Tao Baod3fc38a2018-03-08 16:09:01 -08001367def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001368 """Finalizes the metadata and signs an A/B OTA package.
1369
1370 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1371 that contains the offsets and sizes for the ZIP entries. An example
1372 property-files string is as follows.
1373
1374 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1375
1376 OTA server can pass down this string, in addition to the package URL, to the
1377 system update client. System update client can then fetch individual ZIP
1378 entries (ZIP_STORED) directly at the given offset of the URL.
1379
1380 Args:
1381 metadata: The metadata dict for the package.
1382 input_file: The input ZIP filename that doesn't contain the package METADATA
1383 entry yet.
1384 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001385 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001386 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001387
Tao Baod2ce2ed2018-03-16 12:59:42 -07001388 def ComputeAllPropertyFiles(input_file, needed_property_files):
1389 # Write the current metadata entry with placeholders.
1390 with zipfile.ZipFile(input_file) as input_zip:
1391 for property_files in needed_property_files:
1392 metadata[property_files.name] = property_files.Compute(input_zip)
1393 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001394
Tao Baod2ce2ed2018-03-16 12:59:42 -07001395 if METADATA_NAME in namelist:
1396 common.ZipDelete(input_file, METADATA_NAME)
1397 output_zip = zipfile.ZipFile(input_file, 'a')
1398 WriteMetadata(metadata, output_zip)
1399 common.ZipClose(output_zip)
1400
1401 if OPTIONS.no_signing:
1402 return input_file
1403
Tao Bao491d7e22018-02-21 13:17:22 -08001404 prelim_signing = common.MakeTempFile(suffix='.zip')
1405 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001406 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001407
Tao Baod2ce2ed2018-03-16 12:59:42 -07001408 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1409 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1410 for property_files in needed_property_files:
1411 metadata[property_files.name] = property_files.Finalize(
1412 prelim_signing_zip, len(metadata[property_files.name]))
1413
1414 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1415 # entries, as well as padding the entry headers. We do a preliminary signing
1416 # (with an incomplete metadata entry) to allow that to happen. Then compute
1417 # the ZIP entry offsets, write back the final metadata and do the final
1418 # signing.
1419 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1420 try:
1421 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1422 except PropertyFiles.InsufficientSpaceException:
1423 # Even with the preliminary signing, the entry orders may change
1424 # dramatically, which leads to insufficiently reserved space during the
1425 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1426 # preliminary signing works, based on the already ordered ZIP entries, to
1427 # address the issue.
1428 prelim_signing = ComputeAllPropertyFiles(
1429 prelim_signing, needed_property_files)
1430 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001431
1432 # Replace the METADATA entry.
1433 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001434 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001435 WriteMetadata(metadata, output_zip)
1436 common.ZipClose(output_zip)
1437
1438 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001439 if OPTIONS.no_signing:
1440 output_file = prelim_signing
1441 else:
1442 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001443
1444 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001445 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001446 for property_files in needed_property_files:
1447 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001448
xunchang1cfe2512019-02-19 14:14:48 -08001449 # If requested, dump the metadata to a separate file.
1450 output_metadata_path = OPTIONS.output_metadata_path
1451 if output_metadata_path:
1452 WriteMetadata(metadata, output_metadata_path)
1453
Tao Baofe5b69a2018-03-02 09:47:43 -08001454
Tao Bao491d7e22018-02-21 13:17:22 -08001455def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001456 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1457 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001458
Tao Bao481bab82017-12-21 11:23:09 -08001459 target_api_version = target_info["recovery_api_version"]
1460 source_api_version = source_info["recovery_api_version"]
1461 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001462 logger.warning(
1463 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001464
Tao Bao481bab82017-12-21 11:23:09 -08001465 script = edify_generator.EdifyGenerator(
1466 source_api_version, target_info, fstab=source_info["fstab"])
1467
1468 if target_info.oem_props or source_info.oem_props:
1469 if not OPTIONS.oem_no_mount:
1470 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001471
Tao Baodf3a48b2018-01-10 16:30:43 -08001472 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001473
Tao Bao491d7e22018-02-21 13:17:22 -08001474 if not OPTIONS.no_signing:
1475 staging_file = common.MakeTempFile(suffix='.zip')
1476 else:
1477 staging_file = output_file
1478
1479 output_zip = zipfile.ZipFile(
1480 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1481
Geremy Condra36bd3652014-02-06 19:45:10 -08001482 device_specific = common.DeviceSpecificParams(
1483 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001484 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001485 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001486 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001487 output_zip=output_zip,
1488 script=script,
1489 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001490 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001491
Geremy Condra36bd3652014-02-06 19:45:10 -08001492 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001493 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001494 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001495 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001496 updating_boot = (not OPTIONS.two_step and
1497 (source_boot.data != target_boot.data))
1498
Geremy Condra36bd3652014-02-06 19:45:10 -08001499 target_recovery = common.GetBootableImage(
1500 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001501
Tao Baoe709b092018-02-07 12:40:00 -08001502 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1503 # shared blocks (i.e. some blocks will show up in multiple files' block
1504 # list). We can only allocate such shared blocks to the first "owner", and
1505 # disable imgdiff for all later occurrences.
1506 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1507 target_info.get('ext4_share_dup_blocks') == "true")
1508 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1509 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001510
1511 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1512 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001513 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001514 allow_shared_blocks,
1515 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001516
Tao Bao0582cb62017-12-21 11:47:01 -08001517 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001518 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001519 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001520
Tao Baof8acad12016-07-07 09:09:58 -07001521 # Check the first block of the source system partition for remount R/W only
1522 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001523 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001524 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001525 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1526 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1527 # b) the blocks listed in block map may not contain all the bytes for a given
1528 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001529 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001530 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1531 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001532 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001533 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001534 version=blockimgdiff_version,
1535 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001536
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001537 if HasVendorPartition(target_zip):
1538 if not HasVendorPartition(source_zip):
1539 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001540 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1541 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001542 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1543 "vendor", 4096, target_info)
1544 vendor_tgt = common.GetSparseImage(
1545 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1546 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001547
1548 # Check first block of vendor partition for remount R/W only if
1549 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001550 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001551 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001552 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001553 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001554 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001555 version=blockimgdiff_version,
1556 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001557 else:
1558 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001559
Tao Baobcd1d162017-08-26 13:10:26 -07001560 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001561 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001562
Tao Bao481bab82017-12-21 11:23:09 -08001563 # Assertions (e.g. device properties check).
1564 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001565 device_specific.IncrementalOTA_Assertions()
1566
1567 # Two-step incremental package strategy (in chronological order,
1568 # which is *not* the order in which the generated script has
1569 # things):
1570 #
1571 # if stage is not "2/3" or "3/3":
1572 # do verification on current system
1573 # write recovery image to boot partition
1574 # set stage to "2/3"
1575 # reboot to boot partition and restart recovery
1576 # else if stage is "2/3":
1577 # write recovery image to recovery partition
1578 # set stage to "3/3"
1579 # reboot to recovery partition and restart recovery
1580 # else:
1581 # (stage must be "3/3")
1582 # perform update:
1583 # patch system files, etc.
1584 # force full install of new boot image
1585 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001586 # complete script normally
1587 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001588
1589 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001590 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001591 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001592 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001593 assert fs.fs_type.upper() == "EMMC", \
1594 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001595 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001596 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1597 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001598if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001599""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001600
1601 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1602 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001603 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001604 script.WriteRawImage("/recovery", "recovery.img")
1605 script.AppendExtra("""
1606set_stage("%(bcb_dev)s", "3/3");
1607reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001608else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001609""" % bcb_dev)
1610
Tao Baod42e97e2016-11-30 12:11:57 -08001611 # Stage 1/3: (a) Verify the current system.
1612 script.Comment("Stage 1/3")
1613
Tao Bao6c55a8a2015-04-08 15:30:27 -07001614 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001615 script.Print("Source: {}".format(source_info.fingerprint))
1616 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001617
Geremy Condra36bd3652014-02-06 19:45:10 -08001618 script.Print("Verifying current system...")
1619
1620 device_specific.IncrementalOTA_VerifyBegin()
1621
Tao Bao481bab82017-12-21 11:23:09 -08001622 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001623
Tao Baod8d14be2016-02-04 14:26:02 -08001624 # Check the required cache size (i.e. stashed blocks).
1625 size = []
1626 if system_diff:
1627 size.append(system_diff.required_cache)
1628 if vendor_diff:
1629 size.append(vendor_diff.required_cache)
1630
Geremy Condra36bd3652014-02-06 19:45:10 -08001631 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001632 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001633 d = common.Difference(target_boot, source_boot)
1634 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001635 if d is None:
1636 include_full_boot = True
1637 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1638 else:
1639 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001640
Tao Bao32fcdab2018-10-12 10:30:39 -07001641 logger.info(
1642 "boot target: %d source: %d diff: %d", target_boot.size,
1643 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001644
Tao Bao51216552018-08-26 11:53:15 -07001645 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001646
Tao Bao51216552018-08-26 11:53:15 -07001647 script.PatchPartitionCheck(
1648 "{}:{}:{}:{}".format(
1649 boot_type, boot_device, target_boot.size, target_boot.sha1),
1650 "{}:{}:{}:{}".format(
1651 boot_type, boot_device, source_boot.size, source_boot.sha1))
1652
Tao Baod8d14be2016-02-04 14:26:02 -08001653 size.append(target_boot.size)
1654
1655 if size:
1656 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001657
1658 device_specific.IncrementalOTA_VerifyEnd()
1659
1660 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001661 # Stage 1/3: (b) Write recovery image to /boot.
1662 _WriteRecoveryImageToBoot(script, output_zip)
1663
Geremy Condra36bd3652014-02-06 19:45:10 -08001664 script.AppendExtra("""
1665set_stage("%(bcb_dev)s", "2/3");
1666reboot_now("%(bcb_dev)s", "");
1667else
1668""" % bcb_dev)
1669
Tao Baod42e97e2016-11-30 12:11:57 -08001670 # Stage 3/3: Make changes.
1671 script.Comment("Stage 3/3")
1672
Jesse Zhao75bcea02015-01-06 10:59:53 -08001673 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001674 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001675 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001676 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001677 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1678 if device_specific_diffs:
1679 assert all(isinstance(diff, common.BlockDifference)
1680 for diff in device_specific_diffs), \
1681 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1682 "BlockDifference objects"
1683 for diff in device_specific_diffs:
1684 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001685
Geremy Condra36bd3652014-02-06 19:45:10 -08001686 script.Comment("---- start making changes here ----")
1687
1688 device_specific.IncrementalOTA_InstallBegin()
1689
Yifan Hong10c530d2018-12-27 17:34:18 -08001690 block_diffs = [system_diff]
1691 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001692 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001693 block_diffs.append(vendor_diff)
1694 progress_dict["vendor"] = 0.1
1695 if device_specific_diffs:
1696 block_diffs += device_specific_diffs
1697
1698 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1699 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1700 raise RuntimeError(
1701 "can't generate incremental that disables dynamic partitions")
1702 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1703 info_dict=OPTIONS.target_info_dict,
1704 source_info_dict=OPTIONS.source_info_dict,
1705 block_diffs=block_diffs,
1706 progress_dict=progress_dict)
1707 dynamic_partitions_diff.WriteScript(
1708 script, output_zip, write_verify_script=OPTIONS.verify)
1709 else:
1710 for block_diff in block_diffs:
1711 block_diff.WriteScript(script, output_zip,
1712 progress=progress_dict.get(block_diff.partition),
1713 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001714
1715 if OPTIONS.two_step:
1716 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1717 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001718 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001719
1720 if not OPTIONS.two_step:
1721 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001722 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001723 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001724 script.Print("Installing boot image...")
1725 script.WriteRawImage("/boot", "boot.img")
1726 else:
1727 # Produce the boot image by applying a patch to the current
1728 # contents of the boot partition, and write it back to the
1729 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001730 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001731 script.Print("Patching boot image...")
1732 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001733 script.PatchPartition(
1734 '{}:{}:{}:{}'.format(
1735 boot_type, boot_device, target_boot.size, target_boot.sha1),
1736 '{}:{}:{}:{}'.format(
1737 boot_type, boot_device, source_boot.size, source_boot.sha1),
1738 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001739 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001740 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001741
1742 # Do device-specific installation (eg, write radio image).
1743 device_specific.IncrementalOTA_InstallEnd()
1744
1745 if OPTIONS.extra_script is not None:
1746 script.AppendExtra(OPTIONS.extra_script)
1747
Doug Zongker922206e2014-03-04 13:16:24 -08001748 if OPTIONS.wipe_user_data:
1749 script.Print("Erasing user data...")
1750 script.FormatPartition("/data")
1751
Geremy Condra36bd3652014-02-06 19:45:10 -08001752 if OPTIONS.two_step:
1753 script.AppendExtra("""
1754set_stage("%(bcb_dev)s", "");
1755endif;
1756endif;
1757""" % bcb_dev)
1758
1759 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001760 # For downgrade OTAs, we prefer to use the update-binary in the source
1761 # build that is actually newer than the one in the target build.
1762 if OPTIONS.downgrade:
1763 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1764 else:
1765 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001766 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001767
1768 # We haven't written the metadata entry yet, which will be handled in
1769 # FinalizeMetadata().
1770 common.ZipClose(output_zip)
1771
1772 # Sign the generated zip package unless no_signing is specified.
1773 needed_property_files = (
1774 NonAbOtaPropertyFiles(),
1775 )
1776 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001777
Doug Zongker32b527d2014-03-04 10:03:02 -08001778
Tao Bao15a146a2018-02-21 16:06:59 -08001779def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001780 """Returns a target-files.zip file for generating secondary payload.
1781
1782 Although the original target-files.zip already contains secondary slot
1783 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1784 ones without _other suffix. Note that we cannot instead modify the names in
1785 META/ab_partitions.txt, because there are no matching partitions on device.
1786
1787 For the partitions that don't have secondary images, the ones for primary
1788 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1789 bootloader images in the inactive slot.
1790
1791 Args:
1792 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001793 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001794
1795 Returns:
1796 The filename of the target-files.zip for generating secondary payload.
1797 """
1798 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1799 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1800
Tao Baodba59ee2018-01-09 13:21:02 -08001801 with zipfile.ZipFile(input_file, 'r') as input_zip:
1802 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001803 namelist = input_zip.namelist()
1804
Tao Bao0ff15de2019-03-20 11:26:06 -07001805 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
Tao Baodba59ee2018-01-09 13:21:02 -08001806 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001807 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1808 if info.filename == 'IMAGES/system_other.img':
1809 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1810
1811 # Primary images and friends need to be skipped explicitly.
1812 elif info.filename in ('IMAGES/system.img',
1813 'IMAGES/system.map'):
1814 pass
1815
Tao Bao15a146a2018-02-21 16:06:59 -08001816 # Skip copying the postinstall config if requested.
1817 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1818 pass
1819
Tao Bao12489802018-07-12 14:47:38 -07001820 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001821 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1822
Tao Baof7140c02018-01-30 17:09:24 -08001823 common.ZipClose(target_zip)
1824
1825 return target_file
1826
1827
Tao Bao15a146a2018-02-21 16:06:59 -08001828def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1829 """Returns a target-files.zip that's not containing postinstall_config.txt.
1830
1831 This allows brillo_update_payload script to skip writing all the postinstall
1832 hooks in the generated payload. The input target-files.zip file will be
1833 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1834 contain the postinstall_config.txt entry, the input file will be returned.
1835
1836 Args:
1837 input_file: The input target-files.zip filename.
1838
1839 Returns:
1840 The filename of target-files.zip that doesn't contain postinstall config.
1841 """
1842 # We should only make a copy if postinstall_config entry exists.
1843 with zipfile.ZipFile(input_file, 'r') as input_zip:
1844 if POSTINSTALL_CONFIG not in input_zip.namelist():
1845 return input_file
1846
1847 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1848 shutil.copyfile(input_file, target_file)
1849 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1850 return target_file
1851
1852
Yifan Hong50e79542018-11-08 17:44:12 -08001853def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001854 super_block_devices,
1855 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001856 """Returns a target-files.zip for retrofitting dynamic partitions.
1857
1858 This allows brillo_update_payload to generate an OTA based on the exact
1859 bits on the block devices. Postinstall is disabled.
1860
1861 Args:
1862 input_file: The input target-files.zip filename.
1863 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001864 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001865
1866 Returns:
1867 The filename of target-files.zip with *.img replaced with super_*.img for
1868 each block device in super_block_devices.
1869 """
1870 assert super_block_devices, "No super_block_devices are specified."
1871
1872 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001873 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001874
1875 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1876 shutil.copyfile(input_file, target_file)
1877
1878 with zipfile.ZipFile(input_file, 'r') as input_zip:
1879 namelist = input_zip.namelist()
1880
Yifan Hongb433eba2019-03-06 12:42:53 -08001881 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1882
1883 # Remove partitions from META/ab_partitions.txt that is in
1884 # dynamic_partition_list but not in super_block_devices so that
1885 # brillo_update_payload won't generate update for those logical partitions.
1886 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1887 with open(ab_partitions_file) as f:
1888 ab_partitions_lines = f.readlines()
1889 ab_partitions = [line.strip() for line in ab_partitions_lines]
1890 # Assert that all super_block_devices are in ab_partitions
1891 super_device_not_updated = [partition for partition in super_block_devices
1892 if partition not in ab_partitions]
1893 assert not super_device_not_updated, \
1894 "{} is in super_block_devices but not in {}".format(
1895 super_device_not_updated, AB_PARTITIONS)
1896 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1897 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1898 with open(new_ab_partitions, 'w') as f:
1899 for partition in ab_partitions:
1900 if (partition in dynamic_partition_list and
1901 partition not in super_block_devices):
1902 logger.info("Dropping %s from ab_partitions.txt", partition)
1903 continue
1904 f.write(partition + "\n")
1905 to_delete = [AB_PARTITIONS]
1906
Yifan Hong50e79542018-11-08 17:44:12 -08001907 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001908 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001909
1910 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1911 # is a regular update on devices without dynamic partitions support.
1912 to_delete += [DYNAMIC_PARTITION_INFO]
1913
Tao Bao03fecb62018-11-28 10:59:23 -08001914 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001915 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001916 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001917
1918 common.ZipDelete(target_file, to_delete)
1919
Yifan Hong50e79542018-11-08 17:44:12 -08001920 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1921
1922 # Write super_{foo}.img as {foo}.img.
1923 for src, dst in replace.items():
1924 assert src in namelist, \
1925 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1926 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1927 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1928
Yifan Hongb433eba2019-03-06 12:42:53 -08001929 # Write new ab_partitions.txt file
1930 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1931
Yifan Hong50e79542018-11-08 17:44:12 -08001932 common.ZipClose(target_zip)
1933
1934 return target_file
1935
1936
Tao Baoc098e9e2016-01-07 13:03:56 -08001937def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1938 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001939 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001940 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001941 if not OPTIONS.no_signing:
1942 staging_file = common.MakeTempFile(suffix='.zip')
1943 else:
1944 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001945 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001946 compression=zipfile.ZIP_DEFLATED)
1947
Tao Bao481bab82017-12-21 11:23:09 -08001948 if source_file is not None:
1949 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1950 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1951 else:
1952 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1953 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001954
Tao Bao481bab82017-12-21 11:23:09 -08001955 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001956 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001957
Yifan Hong50e79542018-11-08 17:44:12 -08001958 if OPTIONS.retrofit_dynamic_partitions:
1959 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001960 target_file, target_info.get("super_block_devices").strip().split(),
1961 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001962 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001963 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1964
Tao Bao40b18822018-01-30 18:19:04 -08001965 # Generate payload.
1966 payload = Payload()
1967
1968 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001969 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001970 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001971 else:
1972 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001973 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001974
Tao Bao40b18822018-01-30 18:19:04 -08001975 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001976
Tao Bao40b18822018-01-30 18:19:04 -08001977 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001978 payload_signer = PayloadSigner()
1979 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001980
Tao Bao40b18822018-01-30 18:19:04 -08001981 # Write the payload into output zip.
1982 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001983
Tao Baof7140c02018-01-30 17:09:24 -08001984 # Generate and include the secondary payload that installs secondary images
1985 # (e.g. system_other.img).
1986 if OPTIONS.include_secondary:
1987 # We always include a full payload for the secondary slot, even when
1988 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001989 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1990 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001991 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001992 secondary_payload.Generate(secondary_target_file,
1993 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001994 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001995 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001996
Tianjie Xucfa86222016-03-07 16:31:19 -08001997 # If dm-verity is supported for the device, copy contents of care_map
1998 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001999 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08002000 if (target_info.get("verity") == "true" or
2001 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002002 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
2003 "META/" + x in target_zip.namelist()]
2004
2005 # Adds care_map if either the protobuf format or the plain text one exists.
2006 if care_map_list:
2007 care_map_name = care_map_list[0]
2008 care_map_data = target_zip.read("META/" + care_map_name)
2009 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08002010 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002011 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002012 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002013 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002014 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002015
Tao Baobcd1d162017-08-26 13:10:26 -07002016 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08002017 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07002018
Tao Bao21803d32017-04-19 10:16:09 -07002019 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002020
Tao Baofe5b69a2018-03-02 09:47:43 -08002021 # We haven't written the metadata entry yet, which will be handled in
2022 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002023 common.ZipClose(output_zip)
2024
Tao Bao85f16982018-03-08 16:28:33 -08002025 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2026 # all the info of the latter. However, system updaters and OTA servers need to
2027 # take time to switch to the new flag. We keep both of the flags for
2028 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002029 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002030 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002031 StreamingPropertyFiles(),
2032 )
2033 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002034
Tao Baoc098e9e2016-01-07 13:03:56 -08002035
Doug Zongkereef39442009-04-02 12:14:19 -07002036def main(argv):
2037
2038 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002039 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002040 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002041 elif o in ("-i", "--incremental_from"):
2042 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002043 elif o == "--full_radio":
2044 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002045 elif o == "--full_bootloader":
2046 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002047 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002048 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002049 elif o == "--downgrade":
2050 OPTIONS.downgrade = True
2051 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002052 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002053 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002054 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002055 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002056 elif o == "--oem_no_mount":
2057 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002058 elif o in ("-e", "--extra_script"):
2059 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002060 elif o in ("-t", "--worker_threads"):
2061 if a.isdigit():
2062 OPTIONS.worker_threads = int(a)
2063 else:
2064 raise ValueError("Cannot parse value %r for option %r - only "
2065 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002066 elif o in ("-2", "--two_step"):
2067 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002068 elif o == "--include_secondary":
2069 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002070 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002071 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002072 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002073 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002074 elif o == "--block":
2075 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002076 elif o in ("-b", "--binary"):
2077 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002078 elif o == "--stash_threshold":
2079 try:
2080 OPTIONS.stash_threshold = float(a)
2081 except ValueError:
2082 raise ValueError("Cannot parse value %r for option %r - expecting "
2083 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002084 elif o == "--log_diff":
2085 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002086 elif o == "--payload_signer":
2087 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002088 elif o == "--payload_signer_args":
2089 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002090 elif o == "--extracted_input_target_files":
2091 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002092 elif o == "--skip_postinstall":
2093 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002094 elif o == "--retrofit_dynamic_partitions":
2095 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002096 elif o == "--skip_compatibility_check":
2097 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002098 elif o == "--output_metadata_path":
2099 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002100 else:
2101 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002102 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002103
2104 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002105 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002106 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002107 "package_key=",
2108 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002109 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002110 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002111 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002112 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002113 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002114 "extra_script=",
2115 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002116 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002117 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002118 "no_signing",
2119 "block",
2120 "binary=",
2121 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002122 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002123 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002124 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002125 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002126 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002127 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002128 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002129 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002130 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002131 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002132 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002133 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002134
2135 if len(args) != 2:
2136 common.Usage(__doc__)
2137 sys.exit(1)
2138
Tao Bao32fcdab2018-10-12 10:30:39 -07002139 common.InitLogging()
2140
Tao Bao5d182562016-02-23 11:38:39 -08002141 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002142 # We should only allow downgrading incrementals (as opposed to full).
2143 # Otherwise the device may go back from arbitrary build with this full
2144 # OTA package.
2145 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002146 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002147
Tao Bao2db13852018-01-08 22:28:57 -08002148 # Load the build info dicts from the zip directly or the extracted input
2149 # directory. We don't need to unzip the entire target-files zips, because they
2150 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2151 # When loading the info dicts, we don't need to provide the second parameter
2152 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2153 # some properties with their actual paths, such as 'selinux_fc',
2154 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002155 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002156 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002157 else:
Tao Bao2db13852018-01-08 22:28:57 -08002158 with zipfile.ZipFile(args[0], 'r') as input_zip:
2159 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002160
Tao Bao32fcdab2018-10-12 10:30:39 -07002161 logger.info("--- target info ---")
2162 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002163
2164 # Load the source build dict if applicable.
2165 if OPTIONS.incremental_source is not None:
2166 OPTIONS.target_info_dict = OPTIONS.info_dict
2167 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2168 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2169
Tao Bao32fcdab2018-10-12 10:30:39 -07002170 logger.info("--- source info ---")
2171 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002172
2173 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002174 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2175
Yifan Hong50e79542018-11-08 17:44:12 -08002176 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002177 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002178 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002179 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2180 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002181 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2182 raise common.ExternalError(
2183 "Expect to generate incremental OTA for retrofitting dynamic "
2184 "partitions, but dynamic_partition_retrofit is not set in target "
2185 "build.")
2186 logger.info("Implicitly generating retrofit incremental OTA.")
2187 OPTIONS.retrofit_dynamic_partitions = True
2188
2189 # Skip postinstall for retrofitting dynamic partitions.
2190 if OPTIONS.retrofit_dynamic_partitions:
2191 OPTIONS.skip_postinstall = True
2192
Tao Baoc098e9e2016-01-07 13:03:56 -08002193 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2194
Christian Oderf63e2cd2017-05-01 22:30:15 +02002195 # Use the default key to sign the package if not specified with package_key.
2196 # package_keys are needed on ab_updates, so always define them if an
2197 # ab_update is getting created.
2198 if not OPTIONS.no_signing or ab_update:
2199 if OPTIONS.package_key is None:
2200 OPTIONS.package_key = OPTIONS.info_dict.get(
2201 "default_system_dev_certificate",
2202 "build/target/product/security/testkey")
2203 # Get signing keys
2204 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2205
Tao Baoc098e9e2016-01-07 13:03:56 -08002206 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002207 WriteABOTAPackageWithBrilloScript(
2208 target_file=args[0],
2209 output_file=args[1],
2210 source_file=OPTIONS.incremental_source)
2211
Tao Bao32fcdab2018-10-12 10:30:39 -07002212 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002213 return
2214
Tao Bao2db13852018-01-08 22:28:57 -08002215 # Sanity check the loaded info dicts first.
2216 if OPTIONS.info_dict.get("no_recovery") == "true":
2217 raise common.ExternalError(
2218 "--- target build has specified no recovery ---")
2219
2220 # Non-A/B OTAs rely on /cache partition to store temporary files.
2221 cache_size = OPTIONS.info_dict.get("cache_size")
2222 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002223 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002224 OPTIONS.cache_size = cache_size
2225
Doug Zongker1c390a22009-05-14 19:06:36 -07002226 if OPTIONS.extra_script is not None:
2227 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2228
Dan Willemsencea5cd22017-03-21 14:44:27 -07002229 if OPTIONS.extracted_input is not None:
2230 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002231 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002232 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002233 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002234 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002235
Tao Bao2db13852018-01-08 22:28:57 -08002236 # If the caller explicitly specified the device-specific extensions path via
2237 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2238 # is present in the target target_files. Otherwise, take the path of the file
2239 # from 'tool_extensions' in the info dict and look for that in the local
2240 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002241 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002242 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2243 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002244 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002245 OPTIONS.device_specific = from_input
2246 else:
Tao Bao2db13852018-01-08 22:28:57 -08002247 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002248
Doug Zongker37974732010-09-16 17:44:38 -07002249 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002250 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002251
Tao Bao767e3ac2015-11-10 12:19:19 -08002252 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002253 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002254 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002255 WriteFullOTAPackage(
2256 input_zip,
2257 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002258
Tao Bao32b80dc2018-01-08 22:50:47 -08002259 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002260 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002261 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002262 OPTIONS.source_tmp = common.UnzipTemp(
2263 OPTIONS.incremental_source, UNZIP_PATTERN)
2264 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2265 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002266 WriteBlockIncrementalOTAPackage(
2267 input_zip,
2268 source_zip,
2269 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002270
2271 if OPTIONS.log_diff:
2272 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002273 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002274 target_files_diff.recursiveDiff(
2275 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002276
Tao Bao32fcdab2018-10-12 10:30:39 -07002277 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002278
2279
2280if __name__ == '__main__':
2281 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002282 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002283 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002284 except common.ExternalError:
2285 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002286 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002287 finally:
2288 common.Cleanup()