blob: fe409364635f0e9a6c05ce6c11c33618b2540260 [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 Bao6b0b2f92017-03-05 11:38:11 -0800239UNZIP_PATTERN = ['IMAGES/*', 'META/*']
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
1805 # Additionally unzip 'RADIO/*' if exists.
1806 unzip_pattern = UNZIP_PATTERN[:]
1807 if any([entry.startswith('RADIO/') for entry in namelist]):
1808 unzip_pattern.append('RADIO/*')
1809 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001810
1811 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001812 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1813 if info.filename == 'IMAGES/system_other.img':
1814 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1815
1816 # Primary images and friends need to be skipped explicitly.
1817 elif info.filename in ('IMAGES/system.img',
1818 'IMAGES/system.map'):
1819 pass
1820
Tao Bao15a146a2018-02-21 16:06:59 -08001821 # Skip copying the postinstall config if requested.
1822 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1823 pass
1824
Tao Bao12489802018-07-12 14:47:38 -07001825 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001826 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1827
Tao Baof7140c02018-01-30 17:09:24 -08001828 common.ZipClose(target_zip)
1829
1830 return target_file
1831
1832
Tao Bao15a146a2018-02-21 16:06:59 -08001833def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1834 """Returns a target-files.zip that's not containing postinstall_config.txt.
1835
1836 This allows brillo_update_payload script to skip writing all the postinstall
1837 hooks in the generated payload. The input target-files.zip file will be
1838 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1839 contain the postinstall_config.txt entry, the input file will be returned.
1840
1841 Args:
1842 input_file: The input target-files.zip filename.
1843
1844 Returns:
1845 The filename of target-files.zip that doesn't contain postinstall config.
1846 """
1847 # We should only make a copy if postinstall_config entry exists.
1848 with zipfile.ZipFile(input_file, 'r') as input_zip:
1849 if POSTINSTALL_CONFIG not in input_zip.namelist():
1850 return input_file
1851
1852 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1853 shutil.copyfile(input_file, target_file)
1854 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1855 return target_file
1856
1857
Yifan Hong50e79542018-11-08 17:44:12 -08001858def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001859 super_block_devices,
1860 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001861 """Returns a target-files.zip for retrofitting dynamic partitions.
1862
1863 This allows brillo_update_payload to generate an OTA based on the exact
1864 bits on the block devices. Postinstall is disabled.
1865
1866 Args:
1867 input_file: The input target-files.zip filename.
1868 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001869 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001870
1871 Returns:
1872 The filename of target-files.zip with *.img replaced with super_*.img for
1873 each block device in super_block_devices.
1874 """
1875 assert super_block_devices, "No super_block_devices are specified."
1876
1877 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001878 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001879
1880 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1881 shutil.copyfile(input_file, target_file)
1882
1883 with zipfile.ZipFile(input_file, 'r') as input_zip:
1884 namelist = input_zip.namelist()
1885
Yifan Hongb433eba2019-03-06 12:42:53 -08001886 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1887
1888 # Remove partitions from META/ab_partitions.txt that is in
1889 # dynamic_partition_list but not in super_block_devices so that
1890 # brillo_update_payload won't generate update for those logical partitions.
1891 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1892 with open(ab_partitions_file) as f:
1893 ab_partitions_lines = f.readlines()
1894 ab_partitions = [line.strip() for line in ab_partitions_lines]
1895 # Assert that all super_block_devices are in ab_partitions
1896 super_device_not_updated = [partition for partition in super_block_devices
1897 if partition not in ab_partitions]
1898 assert not super_device_not_updated, \
1899 "{} is in super_block_devices but not in {}".format(
1900 super_device_not_updated, AB_PARTITIONS)
1901 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1902 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1903 with open(new_ab_partitions, 'w') as f:
1904 for partition in ab_partitions:
1905 if (partition in dynamic_partition_list and
1906 partition not in super_block_devices):
1907 logger.info("Dropping %s from ab_partitions.txt", partition)
1908 continue
1909 f.write(partition + "\n")
1910 to_delete = [AB_PARTITIONS]
1911
Yifan Hong50e79542018-11-08 17:44:12 -08001912 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001913 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001914
1915 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1916 # is a regular update on devices without dynamic partitions support.
1917 to_delete += [DYNAMIC_PARTITION_INFO]
1918
Tao Bao03fecb62018-11-28 10:59:23 -08001919 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001920 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001921 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001922
1923 common.ZipDelete(target_file, to_delete)
1924
Yifan Hong50e79542018-11-08 17:44:12 -08001925 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1926
1927 # Write super_{foo}.img as {foo}.img.
1928 for src, dst in replace.items():
1929 assert src in namelist, \
1930 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1931 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1932 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1933
Yifan Hongb433eba2019-03-06 12:42:53 -08001934 # Write new ab_partitions.txt file
1935 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1936
Yifan Hong50e79542018-11-08 17:44:12 -08001937 common.ZipClose(target_zip)
1938
1939 return target_file
1940
1941
Tao Baoc098e9e2016-01-07 13:03:56 -08001942def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1943 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001944 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001945 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001946 if not OPTIONS.no_signing:
1947 staging_file = common.MakeTempFile(suffix='.zip')
1948 else:
1949 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001950 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001951 compression=zipfile.ZIP_DEFLATED)
1952
Tao Bao481bab82017-12-21 11:23:09 -08001953 if source_file is not None:
1954 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1955 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1956 else:
1957 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1958 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001959
Tao Bao481bab82017-12-21 11:23:09 -08001960 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001961 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001962
Yifan Hong50e79542018-11-08 17:44:12 -08001963 if OPTIONS.retrofit_dynamic_partitions:
1964 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001965 target_file, target_info.get("super_block_devices").strip().split(),
1966 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001967 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001968 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1969
Tao Bao40b18822018-01-30 18:19:04 -08001970 # Generate payload.
1971 payload = Payload()
1972
1973 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001974 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001975 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001976 else:
1977 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001978 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001979
Tao Bao40b18822018-01-30 18:19:04 -08001980 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001981
Tao Bao40b18822018-01-30 18:19:04 -08001982 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001983 payload_signer = PayloadSigner()
1984 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001985
Tao Bao40b18822018-01-30 18:19:04 -08001986 # Write the payload into output zip.
1987 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001988
Tao Baof7140c02018-01-30 17:09:24 -08001989 # Generate and include the secondary payload that installs secondary images
1990 # (e.g. system_other.img).
1991 if OPTIONS.include_secondary:
1992 # We always include a full payload for the secondary slot, even when
1993 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001994 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1995 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001996 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001997 secondary_payload.Generate(secondary_target_file,
1998 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001999 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08002000 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08002001
Tianjie Xucfa86222016-03-07 16:31:19 -08002002 # If dm-verity is supported for the device, copy contents of care_map
2003 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07002004 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08002005 if (target_info.get("verity") == "true" or
2006 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002007 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
2008 "META/" + x in target_zip.namelist()]
2009
2010 # Adds care_map if either the protobuf format or the plain text one exists.
2011 if care_map_list:
2012 care_map_name = care_map_list[0]
2013 care_map_data = target_zip.read("META/" + care_map_name)
2014 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08002015 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07002016 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08002017 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08002018 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002019 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07002020
Tao Baobcd1d162017-08-26 13:10:26 -07002021 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08002022 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07002023
Tao Bao21803d32017-04-19 10:16:09 -07002024 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08002025
Tao Baofe5b69a2018-03-02 09:47:43 -08002026 # We haven't written the metadata entry yet, which will be handled in
2027 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08002028 common.ZipClose(output_zip)
2029
Tao Bao85f16982018-03-08 16:28:33 -08002030 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
2031 # all the info of the latter. However, system updaters and OTA servers need to
2032 # take time to switch to the new flag. We keep both of the flags for
2033 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002034 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002035 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002036 StreamingPropertyFiles(),
2037 )
2038 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002039
Tao Baoc098e9e2016-01-07 13:03:56 -08002040
Doug Zongkereef39442009-04-02 12:14:19 -07002041def main(argv):
2042
2043 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002044 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002045 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002046 elif o in ("-i", "--incremental_from"):
2047 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002048 elif o == "--full_radio":
2049 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002050 elif o == "--full_bootloader":
2051 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002052 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002053 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002054 elif o == "--downgrade":
2055 OPTIONS.downgrade = True
2056 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002057 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002058 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002059 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002060 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002061 elif o == "--oem_no_mount":
2062 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002063 elif o in ("-e", "--extra_script"):
2064 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002065 elif o in ("-t", "--worker_threads"):
2066 if a.isdigit():
2067 OPTIONS.worker_threads = int(a)
2068 else:
2069 raise ValueError("Cannot parse value %r for option %r - only "
2070 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002071 elif o in ("-2", "--two_step"):
2072 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002073 elif o == "--include_secondary":
2074 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002075 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002076 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002077 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002078 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002079 elif o == "--block":
2080 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002081 elif o in ("-b", "--binary"):
2082 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002083 elif o == "--stash_threshold":
2084 try:
2085 OPTIONS.stash_threshold = float(a)
2086 except ValueError:
2087 raise ValueError("Cannot parse value %r for option %r - expecting "
2088 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002089 elif o == "--log_diff":
2090 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002091 elif o == "--payload_signer":
2092 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002093 elif o == "--payload_signer_args":
2094 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002095 elif o == "--extracted_input_target_files":
2096 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002097 elif o == "--skip_postinstall":
2098 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002099 elif o == "--retrofit_dynamic_partitions":
2100 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002101 elif o == "--skip_compatibility_check":
2102 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002103 elif o == "--output_metadata_path":
2104 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002105 else:
2106 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002107 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002108
2109 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002110 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002111 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002112 "package_key=",
2113 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002114 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002115 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002116 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002117 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002118 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002119 "extra_script=",
2120 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002121 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002122 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002123 "no_signing",
2124 "block",
2125 "binary=",
2126 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002127 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002128 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002129 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002130 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002131 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002132 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002133 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002134 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002135 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002136 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002137 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002138 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002139
2140 if len(args) != 2:
2141 common.Usage(__doc__)
2142 sys.exit(1)
2143
Tao Bao32fcdab2018-10-12 10:30:39 -07002144 common.InitLogging()
2145
Tao Bao5d182562016-02-23 11:38:39 -08002146 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002147 # We should only allow downgrading incrementals (as opposed to full).
2148 # Otherwise the device may go back from arbitrary build with this full
2149 # OTA package.
2150 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002151 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002152
Tao Bao2db13852018-01-08 22:28:57 -08002153 # Load the build info dicts from the zip directly or the extracted input
2154 # directory. We don't need to unzip the entire target-files zips, because they
2155 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2156 # When loading the info dicts, we don't need to provide the second parameter
2157 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2158 # some properties with their actual paths, such as 'selinux_fc',
2159 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002160 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002161 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002162 else:
Tao Bao2db13852018-01-08 22:28:57 -08002163 with zipfile.ZipFile(args[0], 'r') as input_zip:
2164 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002165
Tao Bao32fcdab2018-10-12 10:30:39 -07002166 logger.info("--- target info ---")
2167 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002168
2169 # Load the source build dict if applicable.
2170 if OPTIONS.incremental_source is not None:
2171 OPTIONS.target_info_dict = OPTIONS.info_dict
2172 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2173 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2174
Tao Bao32fcdab2018-10-12 10:30:39 -07002175 logger.info("--- source info ---")
2176 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002177
2178 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002179 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2180
Yifan Hong50e79542018-11-08 17:44:12 -08002181 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002182 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002183 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002184 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2185 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002186 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2187 raise common.ExternalError(
2188 "Expect to generate incremental OTA for retrofitting dynamic "
2189 "partitions, but dynamic_partition_retrofit is not set in target "
2190 "build.")
2191 logger.info("Implicitly generating retrofit incremental OTA.")
2192 OPTIONS.retrofit_dynamic_partitions = True
2193
2194 # Skip postinstall for retrofitting dynamic partitions.
2195 if OPTIONS.retrofit_dynamic_partitions:
2196 OPTIONS.skip_postinstall = True
2197
Tao Baoc098e9e2016-01-07 13:03:56 -08002198 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2199
Christian Oderf63e2cd2017-05-01 22:30:15 +02002200 # Use the default key to sign the package if not specified with package_key.
2201 # package_keys are needed on ab_updates, so always define them if an
2202 # ab_update is getting created.
2203 if not OPTIONS.no_signing or ab_update:
2204 if OPTIONS.package_key is None:
2205 OPTIONS.package_key = OPTIONS.info_dict.get(
2206 "default_system_dev_certificate",
2207 "build/target/product/security/testkey")
2208 # Get signing keys
2209 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2210
Tao Baoc098e9e2016-01-07 13:03:56 -08002211 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002212 WriteABOTAPackageWithBrilloScript(
2213 target_file=args[0],
2214 output_file=args[1],
2215 source_file=OPTIONS.incremental_source)
2216
Tao Bao32fcdab2018-10-12 10:30:39 -07002217 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002218 return
2219
Tao Bao2db13852018-01-08 22:28:57 -08002220 # Sanity check the loaded info dicts first.
2221 if OPTIONS.info_dict.get("no_recovery") == "true":
2222 raise common.ExternalError(
2223 "--- target build has specified no recovery ---")
2224
2225 # Non-A/B OTAs rely on /cache partition to store temporary files.
2226 cache_size = OPTIONS.info_dict.get("cache_size")
2227 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002228 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002229 OPTIONS.cache_size = cache_size
2230
Doug Zongker1c390a22009-05-14 19:06:36 -07002231 if OPTIONS.extra_script is not None:
2232 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2233
Dan Willemsencea5cd22017-03-21 14:44:27 -07002234 if OPTIONS.extracted_input is not None:
2235 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002236 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002237 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002238 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002239 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002240
Tao Bao2db13852018-01-08 22:28:57 -08002241 # If the caller explicitly specified the device-specific extensions path via
2242 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2243 # is present in the target target_files. Otherwise, take the path of the file
2244 # from 'tool_extensions' in the info dict and look for that in the local
2245 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002246 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002247 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2248 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002249 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002250 OPTIONS.device_specific = from_input
2251 else:
Tao Bao2db13852018-01-08 22:28:57 -08002252 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002253
Doug Zongker37974732010-09-16 17:44:38 -07002254 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002255 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002256
Tao Bao767e3ac2015-11-10 12:19:19 -08002257 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002258 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002259 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002260 WriteFullOTAPackage(
2261 input_zip,
2262 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002263
Tao Bao32b80dc2018-01-08 22:50:47 -08002264 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002265 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002266 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002267 OPTIONS.source_tmp = common.UnzipTemp(
2268 OPTIONS.incremental_source, UNZIP_PATTERN)
2269 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2270 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002271 WriteBlockIncrementalOTAPackage(
2272 input_zip,
2273 source_zip,
2274 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002275
2276 if OPTIONS.log_diff:
2277 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002278 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002279 target_files_diff.recursiveDiff(
2280 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002281
Tao Bao32fcdab2018-10-12 10:30:39 -07002282 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002283
2284
2285if __name__ == '__main__':
2286 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002287 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002288 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002289 except common.ExternalError:
2290 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002291 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002292 finally:
2293 common.Cleanup()