blob: c95aa8d38f0e5701c7a1fa0c3f915d790d3a158f [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'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800238UNZIP_PATTERN = ['IMAGES/*', 'META/*']
Yifan Hong50e79542018-11-08 17:44:12 -0800239SUPER_SPLIT_PATTERN = ['OTA/super_*.img']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800240
Tao Bao2dd1c482017-02-03 16:49:39 -0800241
Tao Bao481bab82017-12-21 11:23:09 -0800242class BuildInfo(object):
243 """A class that holds the information for a given build.
244
245 This class wraps up the property querying for a given source or target build.
246 It abstracts away the logic of handling OEM-specific properties, and caches
247 the commonly used properties such as fingerprint.
248
249 There are two types of info dicts: a) build-time info dict, which is generated
250 at build time (i.e. included in a target_files zip); b) OEM info dict that is
251 specified at package generation time (via command line argument
252 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
253 having "oem_fingerprint_properties" in build-time info dict), all the queries
254 would be answered based on build-time info dict only. Otherwise if using
255 OEM-specific properties, some of them will be calculated from two info dicts.
256
257 Users can query properties similarly as using a dict() (e.g. info['fstab']),
258 or to query build properties via GetBuildProp() or GetVendorBuildProp().
259
260 Attributes:
261 info_dict: The build-time info dict.
262 is_ab: Whether it's a build that uses A/B OTA.
263 oem_dicts: A list of OEM dicts.
264 oem_props: A list of OEM properties that should be read from OEM dicts; None
265 if the build doesn't use any OEM-specific property.
266 fingerprint: The fingerprint of the build, which would be calculated based
267 on OEM properties if applicable.
268 device: The device name, which could come from OEM dicts if applicable.
269 """
270
Steven Laver9e73e822019-01-29 20:20:08 -0800271 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
272 "ro.product.manufacturer", "ro.product.model",
273 "ro.product.name"]
274 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "product_services",
275 "odm", "vendor", "system"]
276
Tao Bao481bab82017-12-21 11:23:09 -0800277 def __init__(self, info_dict, oem_dicts):
278 """Initializes a BuildInfo instance with the given dicts.
279
Tao Bao667c7532018-07-06 10:13:59 -0700280 Note that it only wraps up the given dicts, without making copies.
281
Tao Bao481bab82017-12-21 11:23:09 -0800282 Arguments:
283 info_dict: The build-time info dict.
284 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
285 that it always uses the first dict to calculate the fingerprint or the
286 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700287 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800288 """
289 self.info_dict = info_dict
290 self.oem_dicts = oem_dicts
291
292 self._is_ab = info_dict.get("ab_update") == "true"
293 self._oem_props = info_dict.get("oem_fingerprint_properties")
294
295 if self._oem_props:
296 assert oem_dicts, "OEM source required for this build"
297
298 # These two should be computed only after setting self._oem_props.
299 self._device = self.GetOemProperty("ro.product.device")
300 self._fingerprint = self.CalculateFingerprint()
301
302 @property
303 def is_ab(self):
304 return self._is_ab
305
306 @property
307 def device(self):
308 return self._device
309
310 @property
311 def fingerprint(self):
312 return self._fingerprint
313
314 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700315 def vendor_fingerprint(self):
316 if "vendor.build.prop" not in self.info_dict:
317 return None
318 vendor_build_prop = self.info_dict["vendor.build.prop"]
319 if "ro.vendor.build.fingerprint" in vendor_build_prop:
320 return vendor_build_prop["ro.vendor.build.fingerprint"]
321 if "ro.vendor.build.thumbprint" in vendor_build_prop:
322 return vendor_build_prop["ro.vendor.build.thumbprint"]
323 return None
324
325 @property
Tao Bao481bab82017-12-21 11:23:09 -0800326 def oem_props(self):
327 return self._oem_props
328
329 def __getitem__(self, key):
330 return self.info_dict[key]
331
Tao Bao667c7532018-07-06 10:13:59 -0700332 def __setitem__(self, key, value):
333 self.info_dict[key] = value
334
Tao Bao481bab82017-12-21 11:23:09 -0800335 def get(self, key, default=None):
336 return self.info_dict.get(key, default)
337
Tao Bao667c7532018-07-06 10:13:59 -0700338 def items(self):
339 return self.info_dict.items()
340
Tao Bao481bab82017-12-21 11:23:09 -0800341 def GetBuildProp(self, prop):
342 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800343 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
344 return self._ResolveRoProductBuildProp(prop)
345
Tao Bao481bab82017-12-21 11:23:09 -0800346 try:
347 return self.info_dict.get("build.prop", {})[prop]
348 except KeyError:
349 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
350
Steven Laver9e73e822019-01-29 20:20:08 -0800351 def _ResolveRoProductBuildProp(self, prop):
352 """Resolves the inquired ro.product.* build property"""
353 prop_val = self.info_dict.get("build.prop", {}).get(prop)
354 if prop_val:
355 return prop_val
356
357 source_order_val = self.info_dict.get("build.prop", {}).get(
358 "ro.product.property_source_order")
359 if source_order_val:
360 source_order = source_order_val.split(",")
361 else:
362 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
363
364 # Check that all sources in ro.product.property_source_order are valid
365 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
366 for x in source_order]):
367 raise common.ExternalError(
368 "Invalid ro.product.property_source_order '{}'".format(source_order))
369
370 for source in source_order:
371 source_prop = prop.replace("ro.product", "ro.product.{}".format(source),
372 1)
373 prop_val = self.info_dict.get("{}.build.prop".format(source), {}).get(
374 source_prop)
375 if prop_val:
376 return prop_val
377
378 raise common.ExternalError("couldn't resolve {}".format(prop))
379
Tao Bao481bab82017-12-21 11:23:09 -0800380 def GetVendorBuildProp(self, prop):
381 """Returns the inquired vendor build property."""
382 try:
383 return self.info_dict.get("vendor.build.prop", {})[prop]
384 except KeyError:
385 raise common.ExternalError(
386 "couldn't find %s in vendor.build.prop" % (prop,))
387
388 def GetOemProperty(self, key):
389 if self.oem_props is not None and key in self.oem_props:
390 return self.oem_dicts[0][key]
391 return self.GetBuildProp(key)
392
393 def CalculateFingerprint(self):
394 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800395 try:
396 return self.GetBuildProp("ro.build.fingerprint")
397 except common.ExternalError:
398 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
399 self.GetBuildProp("ro.product.brand"),
400 self.GetBuildProp("ro.product.name"),
401 self.GetBuildProp("ro.product.device"),
402 self.GetBuildProp("ro.build.version.release"),
403 self.GetBuildProp("ro.build.id"),
404 self.GetBuildProp("ro.build.version.incremental"),
405 self.GetBuildProp("ro.build.type"),
406 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800407 return "%s/%s/%s:%s" % (
408 self.GetOemProperty("ro.product.brand"),
409 self.GetOemProperty("ro.product.name"),
410 self.GetOemProperty("ro.product.device"),
411 self.GetBuildProp("ro.build.thumbprint"))
412
413 def WriteMountOemScript(self, script):
414 assert self.oem_props is not None
415 recovery_mount_options = self.info_dict.get("recovery_mount_options")
416 script.Mount("/oem", recovery_mount_options)
417
418 def WriteDeviceAssertions(self, script, oem_no_mount):
419 # Read the property directly if not using OEM properties.
420 if not self.oem_props:
421 script.AssertDevice(self.device)
422 return
423
424 # Otherwise assert OEM properties.
425 if not self.oem_dicts:
426 raise common.ExternalError(
427 "No OEM file provided to answer expected assertions")
428
429 for prop in self.oem_props.split():
430 values = []
431 for oem_dict in self.oem_dicts:
432 if prop in oem_dict:
433 values.append(oem_dict[prop])
434 if not values:
435 raise common.ExternalError(
436 "The OEM file is missing the property %s" % (prop,))
437 script.AssertOemProperty(prop, values, oem_no_mount)
438
439
Tao Baofabe0832018-01-17 15:52:28 -0800440class PayloadSigner(object):
441 """A class that wraps the payload signing works.
442
443 When generating a Payload, hashes of the payload and metadata files will be
444 signed with the device key, either by calling an external payload signer or
445 by calling openssl with the package key. This class provides a unified
446 interface, so that callers can just call PayloadSigner.Sign().
447
448 If an external payload signer has been specified (OPTIONS.payload_signer), it
449 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
450 that the signing key should be provided as part of the payload_signer_args.
451 Otherwise without an external signer, it uses the package key
452 (OPTIONS.package_key) and calls openssl for the signing works.
453 """
454
455 def __init__(self):
456 if OPTIONS.payload_signer is None:
457 # Prepare the payload signing key.
458 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
459 pw = OPTIONS.key_passwords[OPTIONS.package_key]
460
461 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
462 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
463 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
464 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700465 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800466
467 self.signer = "openssl"
468 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
469 "-pkeyopt", "digest:sha256"]
470 else:
471 self.signer = OPTIONS.payload_signer
472 self.signer_args = OPTIONS.payload_signer_args
473
474 def Sign(self, in_file):
475 """Signs the given input file. Returns the output filename."""
476 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
477 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700478 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800479 return out_file
480
481
Tao Bao40b18822018-01-30 18:19:04 -0800482class Payload(object):
483 """Manages the creation and the signing of an A/B OTA Payload."""
484
485 PAYLOAD_BIN = 'payload.bin'
486 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800487 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
488 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800489
Tao Bao667ff572018-02-10 00:02:40 -0800490 def __init__(self, secondary=False):
491 """Initializes a Payload instance.
492
493 Args:
494 secondary: Whether it's generating a secondary payload (default: False).
495 """
Tao Bao40b18822018-01-30 18:19:04 -0800496 self.payload_file = None
497 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800498 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800499
500 def Generate(self, target_file, source_file=None, additional_args=None):
501 """Generates a payload from the given target-files zip(s).
502
503 Args:
504 target_file: The filename of the target build target-files zip.
505 source_file: The filename of the source build target-files zip; or None if
506 generating a full OTA.
507 additional_args: A list of additional args that should be passed to
508 brillo_update_payload script; or None.
509 """
510 if additional_args is None:
511 additional_args = []
512
513 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
514 cmd = ["brillo_update_payload", "generate",
515 "--payload", payload_file,
516 "--target_image", target_file]
517 if source_file is not None:
518 cmd.extend(["--source_image", source_file])
519 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700520 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800521
522 self.payload_file = payload_file
523 self.payload_properties = None
524
525 def Sign(self, payload_signer):
526 """Generates and signs the hashes of the payload and metadata.
527
528 Args:
529 payload_signer: A PayloadSigner() instance that serves the signing work.
530
531 Raises:
532 AssertionError: On any failure when calling brillo_update_payload script.
533 """
534 assert isinstance(payload_signer, PayloadSigner)
535
536 # 1. Generate hashes of the payload and metadata files.
537 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
538 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
539 cmd = ["brillo_update_payload", "hash",
540 "--unsigned_payload", self.payload_file,
541 "--signature_size", "256",
542 "--metadata_hash_file", metadata_sig_file,
543 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700544 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800545
546 # 2. Sign the hashes.
547 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
548 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
549
550 # 3. Insert the signatures back into the payload file.
551 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
552 suffix=".bin")
553 cmd = ["brillo_update_payload", "sign",
554 "--unsigned_payload", self.payload_file,
555 "--payload", signed_payload_file,
556 "--signature_size", "256",
557 "--metadata_signature_file", signed_metadata_sig_file,
558 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700559 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800560
561 # 4. Dump the signed payload properties.
562 properties_file = common.MakeTempFile(prefix="payload-properties-",
563 suffix=".txt")
564 cmd = ["brillo_update_payload", "properties",
565 "--payload", signed_payload_file,
566 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700567 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800568
Tao Bao667ff572018-02-10 00:02:40 -0800569 if self.secondary:
570 with open(properties_file, "a") as f:
571 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
572
Tao Bao40b18822018-01-30 18:19:04 -0800573 if OPTIONS.wipe_user_data:
574 with open(properties_file, "a") as f:
575 f.write("POWERWASH=1\n")
576
577 self.payload_file = signed_payload_file
578 self.payload_properties = properties_file
579
Tao Bao667ff572018-02-10 00:02:40 -0800580 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800581 """Writes the payload to the given zip.
582
583 Args:
584 output_zip: The output ZipFile instance.
585 """
586 assert self.payload_file is not None
587 assert self.payload_properties is not None
588
Tao Bao667ff572018-02-10 00:02:40 -0800589 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800590 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
591 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
592 else:
593 payload_arcname = Payload.PAYLOAD_BIN
594 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
595
Tao Bao40b18822018-01-30 18:19:04 -0800596 # Add the signed payload file and properties into the zip. In order to
597 # support streaming, we pack them as ZIP_STORED. So these entries can be
598 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800599 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800600 compress_type=zipfile.ZIP_STORED)
601 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800602 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800603 compress_type=zipfile.ZIP_STORED)
604
605
Doug Zongkereef39442009-04-02 12:14:19 -0700606def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200607 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700608
Doug Zongker951495f2009-08-14 12:44:19 -0700609 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
610 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700611
612
Tao Bao481bab82017-12-21 11:23:09 -0800613def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800614 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800615 if not oem_source:
616 return None
617
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800618 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800619 for oem_file in oem_source:
620 with open(oem_file) as fp:
621 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800622 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700623
Doug Zongkereef39442009-04-02 12:14:19 -0700624
Tao Baod42e97e2016-11-30 12:11:57 -0800625def _WriteRecoveryImageToBoot(script, output_zip):
626 """Find and write recovery image to /boot in two-step OTA.
627
628 In two-step OTAs, we write recovery image to /boot as the first step so that
629 we can reboot to there and install a new recovery image to /recovery.
630 A special "recovery-two-step.img" will be preferred, which encodes the correct
631 path of "/boot". Otherwise the device may show "device is corrupt" message
632 when booting into /boot.
633
634 Fall back to using the regular recovery.img if the two-step recovery image
635 doesn't exist. Note that rebuilding the special image at this point may be
636 infeasible, because we don't have the desired boot signer and keys when
637 calling ota_from_target_files.py.
638 """
639
640 recovery_two_step_img_name = "recovery-two-step.img"
641 recovery_two_step_img_path = os.path.join(
642 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
643 if os.path.exists(recovery_two_step_img_path):
644 recovery_two_step_img = common.GetBootableImage(
645 recovery_two_step_img_name, recovery_two_step_img_name,
646 OPTIONS.input_tmp, "RECOVERY")
647 common.ZipWriteStr(
648 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700649 logger.info(
650 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800651 script.WriteRawImage("/boot", recovery_two_step_img_name)
652 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700653 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800654 # The "recovery.img" entry has been written into package earlier.
655 script.WriteRawImage("/boot", "recovery.img")
656
657
Doug Zongkerc9253822014-02-04 12:17:58 -0800658def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700659 namelist = [name for name in target_files_zip.namelist()]
660 return ("SYSTEM/recovery-from-boot.p" in namelist or
661 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700662
Tao Bao457cbf62017-03-06 09:56:01 -0800663
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700664def HasVendorPartition(target_files_zip):
665 try:
666 target_files_zip.getinfo("VENDOR/")
667 return True
668 except KeyError:
669 return False
670
Tao Bao457cbf62017-03-06 09:56:01 -0800671
Tao Bao481bab82017-12-21 11:23:09 -0800672def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700673 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800674 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700675
676
Tao Bao481bab82017-12-21 11:23:09 -0800677def WriteFingerprintAssertion(script, target_info, source_info):
678 source_oem_props = source_info.oem_props
679 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700680
Tao Bao481bab82017-12-21 11:23:09 -0800681 if source_oem_props is None and target_oem_props is None:
682 script.AssertSomeFingerprint(
683 source_info.fingerprint, target_info.fingerprint)
684 elif source_oem_props is not None and target_oem_props is not None:
685 script.AssertSomeThumbprint(
686 target_info.GetBuildProp("ro.build.thumbprint"),
687 source_info.GetBuildProp("ro.build.thumbprint"))
688 elif source_oem_props is None and target_oem_props is not None:
689 script.AssertFingerprintOrThumbprint(
690 source_info.fingerprint,
691 target_info.GetBuildProp("ro.build.thumbprint"))
692 else:
693 script.AssertFingerprintOrThumbprint(
694 target_info.fingerprint,
695 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700696
Doug Zongkerfc44a512014-08-26 13:10:25 -0700697
Tao Bao481bab82017-12-21 11:23:09 -0800698def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
699 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700700 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700701
702 Metadata used for on-device compatibility verification is retrieved from
703 target_zip then added to compatibility.zip which is added to the output_zip
704 archive.
705
Tao Baobcd1d162017-08-26 13:10:26 -0700706 Compatibility archive should only be included for devices that have enabled
707 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700708
709 Args:
710 target_zip: Zip file containing the source files to be included for OTA.
711 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800712 target_info: The BuildInfo instance that holds the target build info.
713 source_info: The BuildInfo instance that holds the source build info, if
714 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700715 """
716
Tao Baobcd1d162017-08-26 13:10:26 -0700717 def AddCompatibilityArchive(system_updated, vendor_updated):
718 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700719
Tao Baobcd1d162017-08-26 13:10:26 -0700720 Args:
721 system_updated: If True, the system image will be updated and therefore
722 its metadata should be included.
723 vendor_updated: If True, the vendor image will be updated and therefore
724 its metadata should be included.
725 """
726 # Determine what metadata we need. Files are names relative to META/.
727 compatibility_files = []
728 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
729 system_metadata = ("system_manifest.xml", "system_matrix.xml")
730 if vendor_updated:
731 compatibility_files += vendor_metadata
732 if system_updated:
733 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700734
Tao Baobcd1d162017-08-26 13:10:26 -0700735 # Create new archive.
736 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800737 compatibility_archive_zip = zipfile.ZipFile(
738 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700739
Tao Baobcd1d162017-08-26 13:10:26 -0700740 # Add metadata.
741 for file_name in compatibility_files:
742 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700743
Tao Baobcd1d162017-08-26 13:10:26 -0700744 if target_file_name in target_zip.namelist():
745 data = target_zip.read(target_file_name)
746 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700747
Tao Baobcd1d162017-08-26 13:10:26 -0700748 # Ensure files are written before we copy into output_zip.
749 compatibility_archive_zip.close()
750
751 # Only add the archive if we have any compatibility info.
752 if compatibility_archive_zip.namelist():
753 common.ZipWrite(output_zip, compatibility_archive.name,
754 arcname="compatibility.zip",
755 compress_type=zipfile.ZIP_STORED)
756
757 # Will only proceed if the target has enabled the Treble support (as well as
758 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800759 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700760 return
761
xunchangabfa2652019-02-19 16:27:10 -0800762 # Skip adding the compatibility package as a workaround for b/114240221. The
763 # compatibility will always fail on devices without qualified kernels.
764 if OPTIONS.skip_compatibility_check:
765 return
766
Tao Baobcd1d162017-08-26 13:10:26 -0700767 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800768 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700769 AddCompatibilityArchive(True, True)
770 return
771
Tao Bao481bab82017-12-21 11:23:09 -0800772 source_fp = source_info.fingerprint
773 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700774 system_updated = source_fp != target_fp
775
Tao Baoea6cbd02018-09-05 13:06:37 -0700776 source_fp_vendor = source_info.vendor_fingerprint
777 target_fp_vendor = target_info.vendor_fingerprint
778 # vendor build fingerprints could be possibly blacklisted at build time. For
779 # such a case, we consider the vendor images being changed.
780 if source_fp_vendor is None or target_fp_vendor is None:
781 vendor_updated = True
782 else:
783 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700784
785 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700786
787
Tao Bao491d7e22018-02-21 13:17:22 -0800788def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800789 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700790
Tao Bao481bab82017-12-21 11:23:09 -0800791 # We don't know what version it will be installed on top of. We expect the API
792 # just won't change very often. Similarly for fstab, it might have changed in
793 # the target build.
794 target_api_version = target_info["recovery_api_version"]
795 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700796
Tao Bao481bab82017-12-21 11:23:09 -0800797 if target_info.oem_props and not OPTIONS.oem_no_mount:
798 target_info.WriteMountOemScript(script)
799
Tao Baodf3a48b2018-01-10 16:30:43 -0800800 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700801
Tao Bao491d7e22018-02-21 13:17:22 -0800802 if not OPTIONS.no_signing:
803 staging_file = common.MakeTempFile(suffix='.zip')
804 else:
805 staging_file = output_file
806
807 output_zip = zipfile.ZipFile(
808 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
809
Doug Zongker05d3dea2009-06-22 11:32:31 -0700810 device_specific = common.DeviceSpecificParams(
811 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800812 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700813 output_zip=output_zip,
814 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700815 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700816 metadata=metadata,
817 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700818
Tao Bao457cbf62017-03-06 09:56:01 -0800819 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800820
Tao Bao481bab82017-12-21 11:23:09 -0800821 # Assertions (e.g. downgrade check, device properties check).
822 ts = target_info.GetBuildProp("ro.build.date.utc")
823 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700824 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700825
Tao Bao481bab82017-12-21 11:23:09 -0800826 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700827 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800828
829 # Two-step package strategy (in chronological order, which is *not*
830 # the order in which the generated script has things):
831 #
832 # if stage is not "2/3" or "3/3":
833 # write recovery image to boot partition
834 # set stage to "2/3"
835 # reboot to boot partition and restart recovery
836 # else if stage is "2/3":
837 # write recovery image to recovery partition
838 # set stage to "3/3"
839 # reboot to recovery partition and restart recovery
840 # else:
841 # (stage must be "3/3")
842 # set stage to ""
843 # do normal full package installation:
844 # wipe and install system, boot image, etc.
845 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700846 # complete script normally
847 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800848
849 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
850 OPTIONS.input_tmp, "RECOVERY")
851 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800852 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800853 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800854 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800855 assert fs.fs_type.upper() == "EMMC", \
856 "two-step packages only supported on devices with EMMC /misc partitions"
857 bcb_dev = {"bcb_dev": fs.device}
858 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
859 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700860if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800861""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800862
863 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
864 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800865 script.WriteRawImage("/recovery", "recovery.img")
866 script.AppendExtra("""
867set_stage("%(bcb_dev)s", "3/3");
868reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700869else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800870""" % bcb_dev)
871
Tao Baod42e97e2016-11-30 12:11:57 -0800872 # Stage 3/3: Make changes.
873 script.Comment("Stage 3/3")
874
Tao Bao6c55a8a2015-04-08 15:30:27 -0700875 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800876 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700877
Doug Zongkere5ff5902012-01-17 10:55:37 -0800878 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700879
Doug Zongker01ce19c2014-02-04 13:48:15 -0800880 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700881
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700882 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800883 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700884 if HasVendorPartition(input_zip):
885 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700886
Doug Zongker4b9596f2014-06-09 14:15:45 -0700887 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800888
Tao Baoe709b092018-02-07 12:40:00 -0800889 # See the notes in WriteBlockIncrementalOTAPackage().
890 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
891
Yifan Hong10c530d2018-12-27 17:34:18 -0800892 def GetBlockDifference(partition):
893 # Full OTA is done as an "incremental" against an empty source image. This
894 # has the effect of writing new data from the package to the entire
895 # partition, but lets us reuse the updater code that writes incrementals to
896 # do it.
897 tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
898 allow_shared_blocks)
899 tgt.ResetFileMap()
900 diff = common.BlockDifference(partition, tgt, src=None)
901 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700902
Yifan Hong10c530d2018-12-27 17:34:18 -0800903 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
904 if device_specific_diffs:
905 assert all(isinstance(diff, common.BlockDifference)
906 for diff in device_specific_diffs), \
907 "FullOTA_GetBlockDifferences is not returning a list of " \
908 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800909
Yifan Hong10c530d2018-12-27 17:34:18 -0800910 progress_dict = dict()
911 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700912 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800913 block_diffs.append(GetBlockDifference("vendor"))
914 progress_dict["vendor"] = 0.1
915 if device_specific_diffs:
916 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700917
Yifan Hong10c530d2018-12-27 17:34:18 -0800918 if target_info.get('use_dynamic_partitions') == "true":
919 # Use empty source_info_dict to indicate that all partitions / groups must
920 # be re-added.
921 dynamic_partitions_diff = common.DynamicPartitionsDifference(
922 info_dict=OPTIONS.info_dict,
923 block_diffs=block_diffs,
924 progress_dict=progress_dict)
925 dynamic_partitions_diff.WriteScript(script, output_zip,
926 write_verify_script=OPTIONS.verify)
927 else:
928 for block_diff in block_diffs:
929 block_diff.WriteScript(script, output_zip,
930 progress=progress_dict.get(block_diff.partition),
931 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700932
Tao Bao481bab82017-12-21 11:23:09 -0800933 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700934
Yifan Hong10c530d2018-12-27 17:34:18 -0800935 boot_img = common.GetBootableImage(
936 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800937 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700938 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700939
Doug Zongker01ce19c2014-02-04 13:48:15 -0800940 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700941 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700942
Doug Zongker01ce19c2014-02-04 13:48:15 -0800943 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700944 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700945
Doug Zongker1c390a22009-05-14 19:06:36 -0700946 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700947 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700948
Doug Zongker14833602010-02-02 13:12:04 -0800949 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800950
Doug Zongker922206e2014-03-04 13:16:24 -0800951 if OPTIONS.wipe_user_data:
952 script.ShowProgress(0.1, 10)
953 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700954
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800955 if OPTIONS.two_step:
956 script.AppendExtra("""
957set_stage("%(bcb_dev)s", "");
958""" % bcb_dev)
959 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800960
961 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
962 script.Comment("Stage 1/3")
963 _WriteRecoveryImageToBoot(script, output_zip)
964
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800965 script.AppendExtra("""
966set_stage("%(bcb_dev)s", "2/3");
967reboot_now("%(bcb_dev)s", "");
968endif;
969endif;
970""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800971
Tao Bao5d182562016-02-23 11:38:39 -0800972 script.SetProgress(1)
973 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800974 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800975
976 # We haven't written the metadata entry, which will be done in
977 # FinalizeMetadata.
978 common.ZipClose(output_zip)
979
980 needed_property_files = (
981 NonAbOtaPropertyFiles(),
982 )
983 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700984
Doug Zongkerfc44a512014-08-26 13:10:25 -0700985
xunchang1cfe2512019-02-19 14:14:48 -0800986def WriteMetadata(metadata, output):
987 """Writes the metadata to the zip archive or a file.
988
989 Args:
990 metadata: The metadata dict for the package.
991 output: A ZipFile object or a string of the output file path.
992 """
993
Tao Bao2dd1c482017-02-03 16:49:39 -0800994 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
xunchang1cfe2512019-02-19 14:14:48 -0800995 if isinstance(output, zipfile.ZipFile):
996 common.ZipWriteStr(output, METADATA_NAME, value,
997 compress_type=zipfile.ZIP_STORED)
998 return
999
1000 with open(output, 'w') as f:
1001 f.write(value)
Doug Zongkereef39442009-04-02 12:14:19 -07001002
Doug Zongkerfc44a512014-08-26 13:10:25 -07001003
Tao Bao481bab82017-12-21 11:23:09 -08001004def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -08001005 # Only incremental OTAs are allowed to reach here.
1006 assert OPTIONS.incremental_source is not None
1007
Tao Bao481bab82017-12-21 11:23:09 -08001008 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
1009 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -08001010 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1011
1012 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -08001013 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001014 raise RuntimeError(
1015 "--downgrade or --override_timestamp specified but no downgrade "
1016 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001017 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001018 else:
1019 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001020 raise RuntimeError(
1021 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1022 "Need to specify --override_timestamp OR --downgrade to allow "
1023 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001024
1025
Tao Baodf3a48b2018-01-10 16:30:43 -08001026def GetPackageMetadata(target_info, source_info=None):
1027 """Generates and returns the metadata dict.
1028
1029 It generates a dict() that contains the info to be written into an OTA
1030 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001031 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001032
1033 Args:
1034 target_info: The BuildInfo instance that holds the target build info.
1035 source_info: The BuildInfo instance that holds the source build info, or
1036 None if generating full OTA.
1037
1038 Returns:
1039 A dict to be written into package metadata entry.
1040 """
1041 assert isinstance(target_info, BuildInfo)
1042 assert source_info is None or isinstance(source_info, BuildInfo)
1043
1044 metadata = {
1045 'post-build' : target_info.fingerprint,
1046 'post-build-incremental' : target_info.GetBuildProp(
1047 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001048 'post-sdk-level' : target_info.GetBuildProp(
1049 'ro.build.version.sdk'),
1050 'post-security-patch-level' : target_info.GetBuildProp(
1051 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001052 }
1053
1054 if target_info.is_ab:
1055 metadata['ota-type'] = 'AB'
1056 metadata['ota-required-cache'] = '0'
1057 else:
1058 metadata['ota-type'] = 'BLOCK'
1059
1060 if OPTIONS.wipe_user_data:
1061 metadata['ota-wipe'] = 'yes'
1062
Tao Bao393eeb42019-03-06 16:00:38 -08001063 if OPTIONS.retrofit_dynamic_partitions:
1064 metadata['ota-retrofit-dynamic-partitions'] = 'yes'
1065
Tao Baodf3a48b2018-01-10 16:30:43 -08001066 is_incremental = source_info is not None
1067 if is_incremental:
1068 metadata['pre-build'] = source_info.fingerprint
1069 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1070 'ro.build.version.incremental')
1071 metadata['pre-device'] = source_info.device
1072 else:
1073 metadata['pre-device'] = target_info.device
1074
Tao Baofaa8e0b2018-04-12 14:31:43 -07001075 # Use the actual post-timestamp, even for a downgrade case.
1076 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1077
1078 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001079 if is_incremental:
1080 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001081
1082 return metadata
1083
1084
Tao Baod3fc38a2018-03-08 16:09:01 -08001085class PropertyFiles(object):
1086 """A class that computes the property-files string for an OTA package.
1087
1088 A property-files string is a comma-separated string that contains the
1089 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1090 can be fetched directly with the package URL along with the offset/size info.
1091 These strings can be used for streaming A/B OTAs, or allowing an updater to
1092 download package metadata entry directly, without paying the cost of
1093 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001094
Tao Baocc8e2662018-03-01 19:30:00 -08001095 Computing the final property-files string requires two passes. Because doing
1096 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1097 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1098 values.
1099
1100 This class provides functions to be called for each pass. The general flow is
1101 as follows.
1102
Tao Baod3fc38a2018-03-08 16:09:01 -08001103 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001104 # The first pass, which writes placeholders before doing initial signing.
1105 property_files.Compute()
1106 SignOutput()
1107
1108 # The second pass, by replacing the placeholders with actual data.
1109 property_files.Finalize()
1110 SignOutput()
1111
1112 And the caller can additionally verify the final result.
1113
1114 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001115 """
1116
Tao Baocc8e2662018-03-01 19:30:00 -08001117 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001118 self.name = None
1119 self.required = ()
1120 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001121
Tao Baocc8e2662018-03-01 19:30:00 -08001122 def Compute(self, input_zip):
1123 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001124
Tao Baocc8e2662018-03-01 19:30:00 -08001125 We reserve extra space for the offset and size of the metadata entry itself,
1126 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001127
Tao Baocc8e2662018-03-01 19:30:00 -08001128 Args:
1129 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001130
Tao Baocc8e2662018-03-01 19:30:00 -08001131 Returns:
1132 A string with placeholders for the metadata offset/size info, e.g.
1133 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1134 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001135 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001136
Tao Baod2ce2ed2018-03-16 12:59:42 -07001137 class InsufficientSpaceException(Exception):
1138 pass
1139
Tao Baocc8e2662018-03-01 19:30:00 -08001140 def Finalize(self, input_zip, reserved_length):
1141 """Finalizes a property-files string with actual METADATA offset/size info.
1142
1143 The input ZIP file has been signed, with the ZIP entries in the desired
1144 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1145 the ZIP entry offsets and construct the property-files string with actual
1146 data. Note that during this process, we must pad the property-files string
1147 to the reserved length, so that the METADATA entry size remains the same.
1148 Otherwise the entries' offsets and sizes may change again.
1149
1150 Args:
1151 input_zip: The input ZIP file.
1152 reserved_length: The reserved length of the property-files string during
1153 the call to Compute(). The final string must be no more than this
1154 size.
1155
1156 Returns:
1157 A property-files string including the metadata offset/size info, e.g.
1158 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1159
1160 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001161 InsufficientSpaceException: If the reserved length is insufficient to hold
1162 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001163 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001164 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001165 if len(result) > reserved_length:
1166 raise self.InsufficientSpaceException(
1167 'Insufficient reserved space: reserved={}, actual={}'.format(
1168 reserved_length, len(result)))
1169
Tao Baocc8e2662018-03-01 19:30:00 -08001170 result += ' ' * (reserved_length - len(result))
1171 return result
1172
1173 def Verify(self, input_zip, expected):
1174 """Verifies the input ZIP file contains the expected property-files string.
1175
1176 Args:
1177 input_zip: The input ZIP file.
1178 expected: The property-files string that's computed from Finalize().
1179
1180 Raises:
1181 AssertionError: On finding a mismatch.
1182 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001183 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001184 assert actual == expected, \
1185 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1186
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001187 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1188 """
1189 Constructs the property-files string per request.
1190
1191 Args:
1192 zip_file: The input ZIP file.
1193 reserved_length: The reserved length of the property-files string.
1194
1195 Returns:
1196 A property-files string including the metadata offset/size info, e.g.
1197 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1198 """
Tao Baocc8e2662018-03-01 19:30:00 -08001199
1200 def ComputeEntryOffsetSize(name):
1201 """Computes the zip entry offset and size."""
1202 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001203 offset = info.header_offset
1204 offset += zipfile.sizeFileHeader
1205 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001206 size = info.file_size
1207 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1208
1209 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001210 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001211 for entry in self.required:
1212 tokens.append(ComputeEntryOffsetSize(entry))
1213 for entry in self.optional:
1214 if entry in zip_file.namelist():
1215 tokens.append(ComputeEntryOffsetSize(entry))
1216
1217 # 'META-INF/com/android/metadata' is required. We don't know its actual
1218 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001219 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1220 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1221 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1222 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001223 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001224 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001225 else:
1226 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1227
1228 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001229
Tao Bao85f16982018-03-08 16:28:33 -08001230 def _GetPrecomputed(self, input_zip):
1231 """Computes the additional tokens to be included into the property-files.
1232
1233 This applies to tokens without actual ZIP entries, such as
1234 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1235 that they can download the payload metadata directly with the info.
1236
1237 Args:
1238 input_zip: The input zip file.
1239
1240 Returns:
1241 A list of strings (tokens) to be added to the property-files string.
1242 """
1243 # pylint: disable=no-self-use
1244 # pylint: disable=unused-argument
1245 return []
1246
Tao Baofe5b69a2018-03-02 09:47:43 -08001247
Tao Baod3fc38a2018-03-08 16:09:01 -08001248class StreamingPropertyFiles(PropertyFiles):
1249 """A subclass for computing the property-files for streaming A/B OTAs."""
1250
1251 def __init__(self):
1252 super(StreamingPropertyFiles, self).__init__()
1253 self.name = 'ota-streaming-property-files'
1254 self.required = (
1255 # payload.bin and payload_properties.txt must exist.
1256 'payload.bin',
1257 'payload_properties.txt',
1258 )
1259 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001260 # care_map is available only if dm-verity is enabled.
1261 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001262 'care_map.txt',
1263 # compatibility.zip is available only if target supports Treble.
1264 'compatibility.zip',
1265 )
1266
1267
Tao Bao85f16982018-03-08 16:28:33 -08001268class AbOtaPropertyFiles(StreamingPropertyFiles):
1269 """The property-files for A/B OTA that includes payload_metadata.bin info.
1270
1271 Since P, we expose one more token (aka property-file), in addition to the ones
1272 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1273 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1274 doesn't exist as a separate ZIP entry, but can be used to verify if the
1275 payload can be applied on the given device.
1276
1277 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1278 and the newly added 'ota-property-files' in P. The new token will only be
1279 available in 'ota-property-files'.
1280 """
1281
1282 def __init__(self):
1283 super(AbOtaPropertyFiles, self).__init__()
1284 self.name = 'ota-property-files'
1285
1286 def _GetPrecomputed(self, input_zip):
1287 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1288 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1289
1290 @staticmethod
1291 def _GetPayloadMetadataOffsetAndSize(input_zip):
1292 """Computes the offset and size of the payload metadata for a given package.
1293
1294 (From system/update_engine/update_metadata.proto)
1295 A delta update file contains all the deltas needed to update a system from
1296 one specific version to another specific version. The update format is
1297 represented by this struct pseudocode:
1298
1299 struct delta_update_file {
1300 char magic[4] = "CrAU";
1301 uint64 file_format_version;
1302 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1303
1304 // Only present if format_version > 1:
1305 uint32 metadata_signature_size;
1306
1307 // The Bzip2 compressed DeltaArchiveManifest
1308 char manifest[metadata_signature_size];
1309
1310 // The signature of the metadata (from the beginning of the payload up to
1311 // this location, not including the signature itself). This is a
1312 // serialized Signatures message.
1313 char medatada_signature_message[metadata_signature_size];
1314
1315 // Data blobs for files, no specific format. The specific offset
1316 // and length of each data blob is recorded in the DeltaArchiveManifest.
1317 struct {
1318 char data[];
1319 } blobs[];
1320
1321 // These two are not signed:
1322 uint64 payload_signatures_message_size;
1323 char payload_signatures_message[];
1324 };
1325
1326 'payload-metadata.bin' contains all the bytes from the beginning of the
1327 payload, till the end of 'medatada_signature_message'.
1328 """
1329 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001330 payload_offset = payload_info.header_offset
1331 payload_offset += zipfile.sizeFileHeader
1332 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001333 payload_size = payload_info.file_size
1334
1335 with input_zip.open('payload.bin', 'r') as payload_fp:
1336 header_bin = payload_fp.read(24)
1337
1338 # network byte order (big-endian)
1339 header = struct.unpack("!IQQL", header_bin)
1340
1341 # 'CrAU'
1342 magic = header[0]
1343 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1344
1345 manifest_size = header[2]
1346 metadata_signature_size = header[3]
1347 metadata_total = 24 + manifest_size + metadata_signature_size
1348 assert metadata_total < payload_size
1349
1350 return (payload_offset, metadata_total)
1351
1352
Tao Bao491d7e22018-02-21 13:17:22 -08001353class NonAbOtaPropertyFiles(PropertyFiles):
1354 """The property-files for non-A/B OTA.
1355
1356 For non-A/B OTA, the property-files string contains the info for METADATA
1357 entry, with which a system updater can be fetched the package metadata prior
1358 to downloading the entire package.
1359 """
1360
1361 def __init__(self):
1362 super(NonAbOtaPropertyFiles, self).__init__()
1363 self.name = 'ota-property-files'
1364
1365
Tao Baod3fc38a2018-03-08 16:09:01 -08001366def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001367 """Finalizes the metadata and signs an A/B OTA package.
1368
1369 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1370 that contains the offsets and sizes for the ZIP entries. An example
1371 property-files string is as follows.
1372
1373 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1374
1375 OTA server can pass down this string, in addition to the package URL, to the
1376 system update client. System update client can then fetch individual ZIP
1377 entries (ZIP_STORED) directly at the given offset of the URL.
1378
1379 Args:
1380 metadata: The metadata dict for the package.
1381 input_file: The input ZIP filename that doesn't contain the package METADATA
1382 entry yet.
1383 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001384 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001385 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001386
Tao Baod2ce2ed2018-03-16 12:59:42 -07001387 def ComputeAllPropertyFiles(input_file, needed_property_files):
1388 # Write the current metadata entry with placeholders.
1389 with zipfile.ZipFile(input_file) as input_zip:
1390 for property_files in needed_property_files:
1391 metadata[property_files.name] = property_files.Compute(input_zip)
1392 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001393
Tao Baod2ce2ed2018-03-16 12:59:42 -07001394 if METADATA_NAME in namelist:
1395 common.ZipDelete(input_file, METADATA_NAME)
1396 output_zip = zipfile.ZipFile(input_file, 'a')
1397 WriteMetadata(metadata, output_zip)
1398 common.ZipClose(output_zip)
1399
1400 if OPTIONS.no_signing:
1401 return input_file
1402
Tao Bao491d7e22018-02-21 13:17:22 -08001403 prelim_signing = common.MakeTempFile(suffix='.zip')
1404 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001405 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001406
Tao Baod2ce2ed2018-03-16 12:59:42 -07001407 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1408 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1409 for property_files in needed_property_files:
1410 metadata[property_files.name] = property_files.Finalize(
1411 prelim_signing_zip, len(metadata[property_files.name]))
1412
1413 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1414 # entries, as well as padding the entry headers. We do a preliminary signing
1415 # (with an incomplete metadata entry) to allow that to happen. Then compute
1416 # the ZIP entry offsets, write back the final metadata and do the final
1417 # signing.
1418 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1419 try:
1420 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1421 except PropertyFiles.InsufficientSpaceException:
1422 # Even with the preliminary signing, the entry orders may change
1423 # dramatically, which leads to insufficiently reserved space during the
1424 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1425 # preliminary signing works, based on the already ordered ZIP entries, to
1426 # address the issue.
1427 prelim_signing = ComputeAllPropertyFiles(
1428 prelim_signing, needed_property_files)
1429 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001430
1431 # Replace the METADATA entry.
1432 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001433 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001434 WriteMetadata(metadata, output_zip)
1435 common.ZipClose(output_zip)
1436
1437 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001438 if OPTIONS.no_signing:
1439 output_file = prelim_signing
1440 else:
1441 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001442
1443 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001444 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001445 for property_files in needed_property_files:
1446 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001447
xunchang1cfe2512019-02-19 14:14:48 -08001448 # If requested, dump the metadata to a separate file.
1449 output_metadata_path = OPTIONS.output_metadata_path
1450 if output_metadata_path:
1451 WriteMetadata(metadata, output_metadata_path)
1452
Tao Baofe5b69a2018-03-02 09:47:43 -08001453
Tao Bao491d7e22018-02-21 13:17:22 -08001454def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001455 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1456 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001457
Tao Bao481bab82017-12-21 11:23:09 -08001458 target_api_version = target_info["recovery_api_version"]
1459 source_api_version = source_info["recovery_api_version"]
1460 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001461 logger.warning(
1462 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001463
Tao Bao481bab82017-12-21 11:23:09 -08001464 script = edify_generator.EdifyGenerator(
1465 source_api_version, target_info, fstab=source_info["fstab"])
1466
1467 if target_info.oem_props or source_info.oem_props:
1468 if not OPTIONS.oem_no_mount:
1469 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001470
Tao Baodf3a48b2018-01-10 16:30:43 -08001471 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001472
Tao Bao491d7e22018-02-21 13:17:22 -08001473 if not OPTIONS.no_signing:
1474 staging_file = common.MakeTempFile(suffix='.zip')
1475 else:
1476 staging_file = output_file
1477
1478 output_zip = zipfile.ZipFile(
1479 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1480
Geremy Condra36bd3652014-02-06 19:45:10 -08001481 device_specific = common.DeviceSpecificParams(
1482 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001483 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001484 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001485 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001486 output_zip=output_zip,
1487 script=script,
1488 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001489 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001490
Geremy Condra36bd3652014-02-06 19:45:10 -08001491 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001492 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001493 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001494 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001495 updating_boot = (not OPTIONS.two_step and
1496 (source_boot.data != target_boot.data))
1497
Geremy Condra36bd3652014-02-06 19:45:10 -08001498 target_recovery = common.GetBootableImage(
1499 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001500
Tao Baoe709b092018-02-07 12:40:00 -08001501 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1502 # shared blocks (i.e. some blocks will show up in multiple files' block
1503 # list). We can only allocate such shared blocks to the first "owner", and
1504 # disable imgdiff for all later occurrences.
1505 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1506 target_info.get('ext4_share_dup_blocks') == "true")
1507 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1508 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001509
1510 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1511 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001512 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001513 allow_shared_blocks,
1514 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001515
Tao Bao0582cb62017-12-21 11:47:01 -08001516 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001517 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001518 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001519
Tao Baof8acad12016-07-07 09:09:58 -07001520 # Check the first block of the source system partition for remount R/W only
1521 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001522 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001523 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001524 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1525 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1526 # b) the blocks listed in block map may not contain all the bytes for a given
1527 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001528 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001529 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1530 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001531 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001532 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001533 version=blockimgdiff_version,
1534 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001535
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001536 if HasVendorPartition(target_zip):
1537 if not HasVendorPartition(source_zip):
1538 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001539 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1540 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001541 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1542 "vendor", 4096, target_info)
1543 vendor_tgt = common.GetSparseImage(
1544 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1545 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001546
1547 # Check first block of vendor partition for remount R/W only if
1548 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001549 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001550 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001551 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001552 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001553 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001554 version=blockimgdiff_version,
1555 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001556 else:
1557 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001558
Tao Baobcd1d162017-08-26 13:10:26 -07001559 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001560 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001561
Tao Bao481bab82017-12-21 11:23:09 -08001562 # Assertions (e.g. device properties check).
1563 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001564 device_specific.IncrementalOTA_Assertions()
1565
1566 # Two-step incremental package strategy (in chronological order,
1567 # which is *not* the order in which the generated script has
1568 # things):
1569 #
1570 # if stage is not "2/3" or "3/3":
1571 # do verification on current system
1572 # write recovery image to boot partition
1573 # set stage to "2/3"
1574 # reboot to boot partition and restart recovery
1575 # else if stage is "2/3":
1576 # write recovery image to recovery partition
1577 # set stage to "3/3"
1578 # reboot to recovery partition and restart recovery
1579 # else:
1580 # (stage must be "3/3")
1581 # perform update:
1582 # patch system files, etc.
1583 # force full install of new boot image
1584 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001585 # complete script normally
1586 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001587
1588 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001589 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001590 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001591 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001592 assert fs.fs_type.upper() == "EMMC", \
1593 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001594 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001595 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1596 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001597if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001598""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001599
1600 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1601 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001602 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001603 script.WriteRawImage("/recovery", "recovery.img")
1604 script.AppendExtra("""
1605set_stage("%(bcb_dev)s", "3/3");
1606reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001607else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001608""" % bcb_dev)
1609
Tao Baod42e97e2016-11-30 12:11:57 -08001610 # Stage 1/3: (a) Verify the current system.
1611 script.Comment("Stage 1/3")
1612
Tao Bao6c55a8a2015-04-08 15:30:27 -07001613 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001614 script.Print("Source: {}".format(source_info.fingerprint))
1615 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001616
Geremy Condra36bd3652014-02-06 19:45:10 -08001617 script.Print("Verifying current system...")
1618
1619 device_specific.IncrementalOTA_VerifyBegin()
1620
Tao Bao481bab82017-12-21 11:23:09 -08001621 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001622
Tao Baod8d14be2016-02-04 14:26:02 -08001623 # Check the required cache size (i.e. stashed blocks).
1624 size = []
1625 if system_diff:
1626 size.append(system_diff.required_cache)
1627 if vendor_diff:
1628 size.append(vendor_diff.required_cache)
1629
Geremy Condra36bd3652014-02-06 19:45:10 -08001630 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001631 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001632 d = common.Difference(target_boot, source_boot)
1633 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001634 if d is None:
1635 include_full_boot = True
1636 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1637 else:
1638 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001639
Tao Bao32fcdab2018-10-12 10:30:39 -07001640 logger.info(
1641 "boot target: %d source: %d diff: %d", target_boot.size,
1642 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001643
Tao Bao51216552018-08-26 11:53:15 -07001644 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001645
Tao Bao51216552018-08-26 11:53:15 -07001646 script.PatchPartitionCheck(
1647 "{}:{}:{}:{}".format(
1648 boot_type, boot_device, target_boot.size, target_boot.sha1),
1649 "{}:{}:{}:{}".format(
1650 boot_type, boot_device, source_boot.size, source_boot.sha1))
1651
Tao Baod8d14be2016-02-04 14:26:02 -08001652 size.append(target_boot.size)
1653
1654 if size:
1655 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001656
1657 device_specific.IncrementalOTA_VerifyEnd()
1658
1659 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001660 # Stage 1/3: (b) Write recovery image to /boot.
1661 _WriteRecoveryImageToBoot(script, output_zip)
1662
Geremy Condra36bd3652014-02-06 19:45:10 -08001663 script.AppendExtra("""
1664set_stage("%(bcb_dev)s", "2/3");
1665reboot_now("%(bcb_dev)s", "");
1666else
1667""" % bcb_dev)
1668
Tao Baod42e97e2016-11-30 12:11:57 -08001669 # Stage 3/3: Make changes.
1670 script.Comment("Stage 3/3")
1671
Jesse Zhao75bcea02015-01-06 10:59:53 -08001672 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001673 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001674 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001675 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001676 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1677 if device_specific_diffs:
1678 assert all(isinstance(diff, common.BlockDifference)
1679 for diff in device_specific_diffs), \
1680 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1681 "BlockDifference objects"
1682 for diff in device_specific_diffs:
1683 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001684
Geremy Condra36bd3652014-02-06 19:45:10 -08001685 script.Comment("---- start making changes here ----")
1686
1687 device_specific.IncrementalOTA_InstallBegin()
1688
Yifan Hong10c530d2018-12-27 17:34:18 -08001689 block_diffs = [system_diff]
1690 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001691 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001692 block_diffs.append(vendor_diff)
1693 progress_dict["vendor"] = 0.1
1694 if device_specific_diffs:
1695 block_diffs += device_specific_diffs
1696
1697 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1698 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1699 raise RuntimeError(
1700 "can't generate incremental that disables dynamic partitions")
1701 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1702 info_dict=OPTIONS.target_info_dict,
1703 source_info_dict=OPTIONS.source_info_dict,
1704 block_diffs=block_diffs,
1705 progress_dict=progress_dict)
1706 dynamic_partitions_diff.WriteScript(
1707 script, output_zip, write_verify_script=OPTIONS.verify)
1708 else:
1709 for block_diff in block_diffs:
1710 block_diff.WriteScript(script, output_zip,
1711 progress=progress_dict.get(block_diff.partition),
1712 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001713
1714 if OPTIONS.two_step:
1715 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1716 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001717 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001718
1719 if not OPTIONS.two_step:
1720 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001721 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001722 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001723 script.Print("Installing boot image...")
1724 script.WriteRawImage("/boot", "boot.img")
1725 else:
1726 # Produce the boot image by applying a patch to the current
1727 # contents of the boot partition, and write it back to the
1728 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001729 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001730 script.Print("Patching boot image...")
1731 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001732 script.PatchPartition(
1733 '{}:{}:{}:{}'.format(
1734 boot_type, boot_device, target_boot.size, target_boot.sha1),
1735 '{}:{}:{}:{}'.format(
1736 boot_type, boot_device, source_boot.size, source_boot.sha1),
1737 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001738 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001739 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001740
1741 # Do device-specific installation (eg, write radio image).
1742 device_specific.IncrementalOTA_InstallEnd()
1743
1744 if OPTIONS.extra_script is not None:
1745 script.AppendExtra(OPTIONS.extra_script)
1746
Doug Zongker922206e2014-03-04 13:16:24 -08001747 if OPTIONS.wipe_user_data:
1748 script.Print("Erasing user data...")
1749 script.FormatPartition("/data")
1750
Geremy Condra36bd3652014-02-06 19:45:10 -08001751 if OPTIONS.two_step:
1752 script.AppendExtra("""
1753set_stage("%(bcb_dev)s", "");
1754endif;
1755endif;
1756""" % bcb_dev)
1757
1758 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001759 # For downgrade OTAs, we prefer to use the update-binary in the source
1760 # build that is actually newer than the one in the target build.
1761 if OPTIONS.downgrade:
1762 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1763 else:
1764 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001765 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001766
1767 # We haven't written the metadata entry yet, which will be handled in
1768 # FinalizeMetadata().
1769 common.ZipClose(output_zip)
1770
1771 # Sign the generated zip package unless no_signing is specified.
1772 needed_property_files = (
1773 NonAbOtaPropertyFiles(),
1774 )
1775 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001776
Doug Zongker32b527d2014-03-04 10:03:02 -08001777
Tao Bao15a146a2018-02-21 16:06:59 -08001778def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001779 """Returns a target-files.zip file for generating secondary payload.
1780
1781 Although the original target-files.zip already contains secondary slot
1782 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1783 ones without _other suffix. Note that we cannot instead modify the names in
1784 META/ab_partitions.txt, because there are no matching partitions on device.
1785
1786 For the partitions that don't have secondary images, the ones for primary
1787 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1788 bootloader images in the inactive slot.
1789
1790 Args:
1791 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001792 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001793
1794 Returns:
1795 The filename of the target-files.zip for generating secondary payload.
1796 """
1797 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1798 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1799
Tao Baodba59ee2018-01-09 13:21:02 -08001800 with zipfile.ZipFile(input_file, 'r') as input_zip:
1801 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001802 namelist = input_zip.namelist()
1803
1804 # Additionally unzip 'RADIO/*' if exists.
1805 unzip_pattern = UNZIP_PATTERN[:]
1806 if any([entry.startswith('RADIO/') for entry in namelist]):
1807 unzip_pattern.append('RADIO/*')
1808 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001809
1810 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001811 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1812 if info.filename == 'IMAGES/system_other.img':
1813 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1814
1815 # Primary images and friends need to be skipped explicitly.
1816 elif info.filename in ('IMAGES/system.img',
1817 'IMAGES/system.map'):
1818 pass
1819
Tao Bao15a146a2018-02-21 16:06:59 -08001820 # Skip copying the postinstall config if requested.
1821 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1822 pass
1823
Tao Bao12489802018-07-12 14:47:38 -07001824 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001825 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1826
Tao Baof7140c02018-01-30 17:09:24 -08001827 common.ZipClose(target_zip)
1828
1829 return target_file
1830
1831
Tao Bao15a146a2018-02-21 16:06:59 -08001832def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1833 """Returns a target-files.zip that's not containing postinstall_config.txt.
1834
1835 This allows brillo_update_payload script to skip writing all the postinstall
1836 hooks in the generated payload. The input target-files.zip file will be
1837 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1838 contain the postinstall_config.txt entry, the input file will be returned.
1839
1840 Args:
1841 input_file: The input target-files.zip filename.
1842
1843 Returns:
1844 The filename of target-files.zip that doesn't contain postinstall config.
1845 """
1846 # We should only make a copy if postinstall_config entry exists.
1847 with zipfile.ZipFile(input_file, 'r') as input_zip:
1848 if POSTINSTALL_CONFIG not in input_zip.namelist():
1849 return input_file
1850
1851 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1852 shutil.copyfile(input_file, target_file)
1853 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1854 return target_file
1855
1856
Yifan Hong50e79542018-11-08 17:44:12 -08001857def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
1858 super_block_devices):
1859 """Returns a target-files.zip for retrofitting dynamic partitions.
1860
1861 This allows brillo_update_payload to generate an OTA based on the exact
1862 bits on the block devices. Postinstall is disabled.
1863
1864 Args:
1865 input_file: The input target-files.zip filename.
1866 super_block_devices: The list of super block devices
1867
1868 Returns:
1869 The filename of target-files.zip with *.img replaced with super_*.img for
1870 each block device in super_block_devices.
1871 """
1872 assert super_block_devices, "No super_block_devices are specified."
1873
1874 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001875 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001876
1877 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1878 shutil.copyfile(input_file, target_file)
1879
1880 with zipfile.ZipFile(input_file, 'r') as input_zip:
1881 namelist = input_zip.namelist()
1882
1883 # Always skip postinstall for a retrofit update.
1884 to_delete = [POSTINSTALL_CONFIG]
1885
1886 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1887 # is a regular update on devices without dynamic partitions support.
1888 to_delete += [DYNAMIC_PARTITION_INFO]
1889
Tao Bao03fecb62018-11-28 10:59:23 -08001890 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001891 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001892 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001893
1894 common.ZipDelete(target_file, to_delete)
1895
1896 input_tmp = common.UnzipTemp(input_file, SUPER_SPLIT_PATTERN)
1897 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1898
1899 # Write super_{foo}.img as {foo}.img.
1900 for src, dst in replace.items():
1901 assert src in namelist, \
1902 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1903 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1904 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1905
1906 common.ZipClose(target_zip)
1907
1908 return target_file
1909
1910
Tao Baoc098e9e2016-01-07 13:03:56 -08001911def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1912 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001913 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001914 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001915 if not OPTIONS.no_signing:
1916 staging_file = common.MakeTempFile(suffix='.zip')
1917 else:
1918 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001919 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001920 compression=zipfile.ZIP_DEFLATED)
1921
Tao Bao481bab82017-12-21 11:23:09 -08001922 if source_file is not None:
1923 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1924 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1925 else:
1926 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1927 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001928
Tao Bao481bab82017-12-21 11:23:09 -08001929 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001930 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001931
Yifan Hong50e79542018-11-08 17:44:12 -08001932 if OPTIONS.retrofit_dynamic_partitions:
1933 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
1934 target_file, target_info.get("super_block_devices").strip().split())
1935 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001936 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1937
Tao Bao40b18822018-01-30 18:19:04 -08001938 # Generate payload.
1939 payload = Payload()
1940
1941 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001942 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001943 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001944 else:
1945 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001946 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001947
Tao Bao40b18822018-01-30 18:19:04 -08001948 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001949
Tao Bao40b18822018-01-30 18:19:04 -08001950 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001951 payload_signer = PayloadSigner()
1952 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001953
Tao Bao40b18822018-01-30 18:19:04 -08001954 # Write the payload into output zip.
1955 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001956
Tao Baof7140c02018-01-30 17:09:24 -08001957 # Generate and include the secondary payload that installs secondary images
1958 # (e.g. system_other.img).
1959 if OPTIONS.include_secondary:
1960 # We always include a full payload for the secondary slot, even when
1961 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001962 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1963 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001964 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001965 secondary_payload.Generate(secondary_target_file,
1966 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001967 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001968 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001969
Tianjie Xucfa86222016-03-07 16:31:19 -08001970 # If dm-verity is supported for the device, copy contents of care_map
1971 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001972 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001973 if (target_info.get("verity") == "true" or
1974 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001975 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1976 "META/" + x in target_zip.namelist()]
1977
1978 # Adds care_map if either the protobuf format or the plain text one exists.
1979 if care_map_list:
1980 care_map_name = care_map_list[0]
1981 care_map_data = target_zip.read("META/" + care_map_name)
1982 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001983 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001984 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001985 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001986 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001987 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001988
Tao Baobcd1d162017-08-26 13:10:26 -07001989 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001990 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001991
Tao Bao21803d32017-04-19 10:16:09 -07001992 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001993
Tao Baofe5b69a2018-03-02 09:47:43 -08001994 # We haven't written the metadata entry yet, which will be handled in
1995 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001996 common.ZipClose(output_zip)
1997
Tao Bao85f16982018-03-08 16:28:33 -08001998 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1999 # all the info of the latter. However, system updaters and OTA servers need to
2000 # take time to switch to the new flag. We keep both of the flags for
2001 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002002 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002003 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002004 StreamingPropertyFiles(),
2005 )
2006 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002007
Tao Baoc098e9e2016-01-07 13:03:56 -08002008
Doug Zongkereef39442009-04-02 12:14:19 -07002009def main(argv):
2010
2011 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002012 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002013 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002014 elif o in ("-i", "--incremental_from"):
2015 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002016 elif o == "--full_radio":
2017 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002018 elif o == "--full_bootloader":
2019 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002020 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002021 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002022 elif o == "--downgrade":
2023 OPTIONS.downgrade = True
2024 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002025 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002026 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002027 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002028 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002029 elif o == "--oem_no_mount":
2030 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002031 elif o in ("-e", "--extra_script"):
2032 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002033 elif o in ("-t", "--worker_threads"):
2034 if a.isdigit():
2035 OPTIONS.worker_threads = int(a)
2036 else:
2037 raise ValueError("Cannot parse value %r for option %r - only "
2038 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002039 elif o in ("-2", "--two_step"):
2040 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002041 elif o == "--include_secondary":
2042 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002043 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002044 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002045 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002046 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002047 elif o == "--block":
2048 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002049 elif o in ("-b", "--binary"):
2050 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002051 elif o == "--stash_threshold":
2052 try:
2053 OPTIONS.stash_threshold = float(a)
2054 except ValueError:
2055 raise ValueError("Cannot parse value %r for option %r - expecting "
2056 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002057 elif o == "--log_diff":
2058 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002059 elif o == "--payload_signer":
2060 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002061 elif o == "--payload_signer_args":
2062 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002063 elif o == "--extracted_input_target_files":
2064 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002065 elif o == "--skip_postinstall":
2066 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002067 elif o == "--retrofit_dynamic_partitions":
2068 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002069 elif o == "--skip_compatibility_check":
2070 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002071 elif o == "--output_metadata_path":
2072 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002073 else:
2074 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002075 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002076
2077 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002078 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002079 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002080 "package_key=",
2081 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002082 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002083 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002084 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002085 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002086 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002087 "extra_script=",
2088 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002089 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002090 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002091 "no_signing",
2092 "block",
2093 "binary=",
2094 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002095 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002096 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002097 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002098 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002099 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002100 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002101 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002102 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002103 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002104 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002105 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002106 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002107
2108 if len(args) != 2:
2109 common.Usage(__doc__)
2110 sys.exit(1)
2111
Tao Bao32fcdab2018-10-12 10:30:39 -07002112 common.InitLogging()
2113
Tao Bao5d182562016-02-23 11:38:39 -08002114 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002115 # We should only allow downgrading incrementals (as opposed to full).
2116 # Otherwise the device may go back from arbitrary build with this full
2117 # OTA package.
2118 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002119 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002120
Tao Bao2db13852018-01-08 22:28:57 -08002121 # Load the build info dicts from the zip directly or the extracted input
2122 # directory. We don't need to unzip the entire target-files zips, because they
2123 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2124 # When loading the info dicts, we don't need to provide the second parameter
2125 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2126 # some properties with their actual paths, such as 'selinux_fc',
2127 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002128 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002129 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002130 else:
Tao Bao2db13852018-01-08 22:28:57 -08002131 with zipfile.ZipFile(args[0], 'r') as input_zip:
2132 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002133
Tao Bao32fcdab2018-10-12 10:30:39 -07002134 logger.info("--- target info ---")
2135 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002136
2137 # Load the source build dict if applicable.
2138 if OPTIONS.incremental_source is not None:
2139 OPTIONS.target_info_dict = OPTIONS.info_dict
2140 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2141 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2142
Tao Bao32fcdab2018-10-12 10:30:39 -07002143 logger.info("--- source info ---")
2144 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002145
2146 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002147 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2148
Yifan Hong50e79542018-11-08 17:44:12 -08002149 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002150 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002151 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002152 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2153 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002154 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2155 raise common.ExternalError(
2156 "Expect to generate incremental OTA for retrofitting dynamic "
2157 "partitions, but dynamic_partition_retrofit is not set in target "
2158 "build.")
2159 logger.info("Implicitly generating retrofit incremental OTA.")
2160 OPTIONS.retrofit_dynamic_partitions = True
2161
2162 # Skip postinstall for retrofitting dynamic partitions.
2163 if OPTIONS.retrofit_dynamic_partitions:
2164 OPTIONS.skip_postinstall = True
2165
Tao Baoc098e9e2016-01-07 13:03:56 -08002166 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2167
Christian Oderf63e2cd2017-05-01 22:30:15 +02002168 # Use the default key to sign the package if not specified with package_key.
2169 # package_keys are needed on ab_updates, so always define them if an
2170 # ab_update is getting created.
2171 if not OPTIONS.no_signing or ab_update:
2172 if OPTIONS.package_key is None:
2173 OPTIONS.package_key = OPTIONS.info_dict.get(
2174 "default_system_dev_certificate",
2175 "build/target/product/security/testkey")
2176 # Get signing keys
2177 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2178
Tao Baoc098e9e2016-01-07 13:03:56 -08002179 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002180 WriteABOTAPackageWithBrilloScript(
2181 target_file=args[0],
2182 output_file=args[1],
2183 source_file=OPTIONS.incremental_source)
2184
Tao Bao32fcdab2018-10-12 10:30:39 -07002185 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002186 return
2187
Tao Bao2db13852018-01-08 22:28:57 -08002188 # Sanity check the loaded info dicts first.
2189 if OPTIONS.info_dict.get("no_recovery") == "true":
2190 raise common.ExternalError(
2191 "--- target build has specified no recovery ---")
2192
2193 # Non-A/B OTAs rely on /cache partition to store temporary files.
2194 cache_size = OPTIONS.info_dict.get("cache_size")
2195 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002196 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002197 OPTIONS.cache_size = cache_size
2198
Doug Zongker1c390a22009-05-14 19:06:36 -07002199 if OPTIONS.extra_script is not None:
2200 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2201
Dan Willemsencea5cd22017-03-21 14:44:27 -07002202 if OPTIONS.extracted_input is not None:
2203 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002204 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002205 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002206 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002207 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002208
Tao Bao2db13852018-01-08 22:28:57 -08002209 # If the caller explicitly specified the device-specific extensions path via
2210 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2211 # is present in the target target_files. Otherwise, take the path of the file
2212 # from 'tool_extensions' in the info dict and look for that in the local
2213 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002214 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002215 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2216 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002217 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002218 OPTIONS.device_specific = from_input
2219 else:
Tao Bao2db13852018-01-08 22:28:57 -08002220 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002221
Doug Zongker37974732010-09-16 17:44:38 -07002222 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002223 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002224
Tao Bao767e3ac2015-11-10 12:19:19 -08002225 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002226 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002227 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002228 WriteFullOTAPackage(
2229 input_zip,
2230 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002231
Tao Bao32b80dc2018-01-08 22:50:47 -08002232 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002233 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002234 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002235 OPTIONS.source_tmp = common.UnzipTemp(
2236 OPTIONS.incremental_source, UNZIP_PATTERN)
2237 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2238 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002239 WriteBlockIncrementalOTAPackage(
2240 input_zip,
2241 source_zip,
2242 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002243
2244 if OPTIONS.log_diff:
2245 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002246 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002247 target_files_diff.recursiveDiff(
2248 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002249
Tao Bao32fcdab2018-10-12 10:30:39 -07002250 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002251
2252
2253if __name__ == '__main__':
2254 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002255 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002256 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002257 except common.ExternalError:
2258 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002259 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002260 finally:
2261 common.Cleanup()