blob: fa9e2e9b1b8774f1bbac6cf11e0c3e4f53056b2d [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
1063 is_incremental = source_info is not None
1064 if is_incremental:
1065 metadata['pre-build'] = source_info.fingerprint
1066 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1067 'ro.build.version.incremental')
1068 metadata['pre-device'] = source_info.device
1069 else:
1070 metadata['pre-device'] = target_info.device
1071
Tao Baofaa8e0b2018-04-12 14:31:43 -07001072 # Use the actual post-timestamp, even for a downgrade case.
1073 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1074
1075 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001076 if is_incremental:
1077 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001078
1079 return metadata
1080
1081
Tao Baod3fc38a2018-03-08 16:09:01 -08001082class PropertyFiles(object):
1083 """A class that computes the property-files string for an OTA package.
1084
1085 A property-files string is a comma-separated string that contains the
1086 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1087 can be fetched directly with the package URL along with the offset/size info.
1088 These strings can be used for streaming A/B OTAs, or allowing an updater to
1089 download package metadata entry directly, without paying the cost of
1090 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001091
Tao Baocc8e2662018-03-01 19:30:00 -08001092 Computing the final property-files string requires two passes. Because doing
1093 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1094 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1095 values.
1096
1097 This class provides functions to be called for each pass. The general flow is
1098 as follows.
1099
Tao Baod3fc38a2018-03-08 16:09:01 -08001100 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001101 # The first pass, which writes placeholders before doing initial signing.
1102 property_files.Compute()
1103 SignOutput()
1104
1105 # The second pass, by replacing the placeholders with actual data.
1106 property_files.Finalize()
1107 SignOutput()
1108
1109 And the caller can additionally verify the final result.
1110
1111 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001112 """
1113
Tao Baocc8e2662018-03-01 19:30:00 -08001114 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001115 self.name = None
1116 self.required = ()
1117 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001118
Tao Baocc8e2662018-03-01 19:30:00 -08001119 def Compute(self, input_zip):
1120 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001121
Tao Baocc8e2662018-03-01 19:30:00 -08001122 We reserve extra space for the offset and size of the metadata entry itself,
1123 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001124
Tao Baocc8e2662018-03-01 19:30:00 -08001125 Args:
1126 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001127
Tao Baocc8e2662018-03-01 19:30:00 -08001128 Returns:
1129 A string with placeholders for the metadata offset/size info, e.g.
1130 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1131 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001132 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001133
Tao Baod2ce2ed2018-03-16 12:59:42 -07001134 class InsufficientSpaceException(Exception):
1135 pass
1136
Tao Baocc8e2662018-03-01 19:30:00 -08001137 def Finalize(self, input_zip, reserved_length):
1138 """Finalizes a property-files string with actual METADATA offset/size info.
1139
1140 The input ZIP file has been signed, with the ZIP entries in the desired
1141 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1142 the ZIP entry offsets and construct the property-files string with actual
1143 data. Note that during this process, we must pad the property-files string
1144 to the reserved length, so that the METADATA entry size remains the same.
1145 Otherwise the entries' offsets and sizes may change again.
1146
1147 Args:
1148 input_zip: The input ZIP file.
1149 reserved_length: The reserved length of the property-files string during
1150 the call to Compute(). The final string must be no more than this
1151 size.
1152
1153 Returns:
1154 A property-files string including the metadata offset/size info, e.g.
1155 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1156
1157 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001158 InsufficientSpaceException: If the reserved length is insufficient to hold
1159 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001160 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001161 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001162 if len(result) > reserved_length:
1163 raise self.InsufficientSpaceException(
1164 'Insufficient reserved space: reserved={}, actual={}'.format(
1165 reserved_length, len(result)))
1166
Tao Baocc8e2662018-03-01 19:30:00 -08001167 result += ' ' * (reserved_length - len(result))
1168 return result
1169
1170 def Verify(self, input_zip, expected):
1171 """Verifies the input ZIP file contains the expected property-files string.
1172
1173 Args:
1174 input_zip: The input ZIP file.
1175 expected: The property-files string that's computed from Finalize().
1176
1177 Raises:
1178 AssertionError: On finding a mismatch.
1179 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001180 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001181 assert actual == expected, \
1182 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1183
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001184 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1185 """
1186 Constructs the property-files string per request.
1187
1188 Args:
1189 zip_file: The input ZIP file.
1190 reserved_length: The reserved length of the property-files string.
1191
1192 Returns:
1193 A property-files string including the metadata offset/size info, e.g.
1194 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1195 """
Tao Baocc8e2662018-03-01 19:30:00 -08001196
1197 def ComputeEntryOffsetSize(name):
1198 """Computes the zip entry offset and size."""
1199 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001200 offset = info.header_offset
1201 offset += zipfile.sizeFileHeader
1202 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001203 size = info.file_size
1204 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1205
1206 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001207 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001208 for entry in self.required:
1209 tokens.append(ComputeEntryOffsetSize(entry))
1210 for entry in self.optional:
1211 if entry in zip_file.namelist():
1212 tokens.append(ComputeEntryOffsetSize(entry))
1213
1214 # 'META-INF/com/android/metadata' is required. We don't know its actual
1215 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001216 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1217 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1218 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1219 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001220 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001221 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001222 else:
1223 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1224
1225 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001226
Tao Bao85f16982018-03-08 16:28:33 -08001227 def _GetPrecomputed(self, input_zip):
1228 """Computes the additional tokens to be included into the property-files.
1229
1230 This applies to tokens without actual ZIP entries, such as
1231 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1232 that they can download the payload metadata directly with the info.
1233
1234 Args:
1235 input_zip: The input zip file.
1236
1237 Returns:
1238 A list of strings (tokens) to be added to the property-files string.
1239 """
1240 # pylint: disable=no-self-use
1241 # pylint: disable=unused-argument
1242 return []
1243
Tao Baofe5b69a2018-03-02 09:47:43 -08001244
Tao Baod3fc38a2018-03-08 16:09:01 -08001245class StreamingPropertyFiles(PropertyFiles):
1246 """A subclass for computing the property-files for streaming A/B OTAs."""
1247
1248 def __init__(self):
1249 super(StreamingPropertyFiles, self).__init__()
1250 self.name = 'ota-streaming-property-files'
1251 self.required = (
1252 # payload.bin and payload_properties.txt must exist.
1253 'payload.bin',
1254 'payload_properties.txt',
1255 )
1256 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001257 # care_map is available only if dm-verity is enabled.
1258 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001259 'care_map.txt',
1260 # compatibility.zip is available only if target supports Treble.
1261 'compatibility.zip',
1262 )
1263
1264
Tao Bao85f16982018-03-08 16:28:33 -08001265class AbOtaPropertyFiles(StreamingPropertyFiles):
1266 """The property-files for A/B OTA that includes payload_metadata.bin info.
1267
1268 Since P, we expose one more token (aka property-file), in addition to the ones
1269 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1270 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1271 doesn't exist as a separate ZIP entry, but can be used to verify if the
1272 payload can be applied on the given device.
1273
1274 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1275 and the newly added 'ota-property-files' in P. The new token will only be
1276 available in 'ota-property-files'.
1277 """
1278
1279 def __init__(self):
1280 super(AbOtaPropertyFiles, self).__init__()
1281 self.name = 'ota-property-files'
1282
1283 def _GetPrecomputed(self, input_zip):
1284 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1285 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1286
1287 @staticmethod
1288 def _GetPayloadMetadataOffsetAndSize(input_zip):
1289 """Computes the offset and size of the payload metadata for a given package.
1290
1291 (From system/update_engine/update_metadata.proto)
1292 A delta update file contains all the deltas needed to update a system from
1293 one specific version to another specific version. The update format is
1294 represented by this struct pseudocode:
1295
1296 struct delta_update_file {
1297 char magic[4] = "CrAU";
1298 uint64 file_format_version;
1299 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1300
1301 // Only present if format_version > 1:
1302 uint32 metadata_signature_size;
1303
1304 // The Bzip2 compressed DeltaArchiveManifest
1305 char manifest[metadata_signature_size];
1306
1307 // The signature of the metadata (from the beginning of the payload up to
1308 // this location, not including the signature itself). This is a
1309 // serialized Signatures message.
1310 char medatada_signature_message[metadata_signature_size];
1311
1312 // Data blobs for files, no specific format. The specific offset
1313 // and length of each data blob is recorded in the DeltaArchiveManifest.
1314 struct {
1315 char data[];
1316 } blobs[];
1317
1318 // These two are not signed:
1319 uint64 payload_signatures_message_size;
1320 char payload_signatures_message[];
1321 };
1322
1323 'payload-metadata.bin' contains all the bytes from the beginning of the
1324 payload, till the end of 'medatada_signature_message'.
1325 """
1326 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001327 payload_offset = payload_info.header_offset
1328 payload_offset += zipfile.sizeFileHeader
1329 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001330 payload_size = payload_info.file_size
1331
1332 with input_zip.open('payload.bin', 'r') as payload_fp:
1333 header_bin = payload_fp.read(24)
1334
1335 # network byte order (big-endian)
1336 header = struct.unpack("!IQQL", header_bin)
1337
1338 # 'CrAU'
1339 magic = header[0]
1340 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1341
1342 manifest_size = header[2]
1343 metadata_signature_size = header[3]
1344 metadata_total = 24 + manifest_size + metadata_signature_size
1345 assert metadata_total < payload_size
1346
1347 return (payload_offset, metadata_total)
1348
1349
Tao Bao491d7e22018-02-21 13:17:22 -08001350class NonAbOtaPropertyFiles(PropertyFiles):
1351 """The property-files for non-A/B OTA.
1352
1353 For non-A/B OTA, the property-files string contains the info for METADATA
1354 entry, with which a system updater can be fetched the package metadata prior
1355 to downloading the entire package.
1356 """
1357
1358 def __init__(self):
1359 super(NonAbOtaPropertyFiles, self).__init__()
1360 self.name = 'ota-property-files'
1361
1362
Tao Baod3fc38a2018-03-08 16:09:01 -08001363def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001364 """Finalizes the metadata and signs an A/B OTA package.
1365
1366 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1367 that contains the offsets and sizes for the ZIP entries. An example
1368 property-files string is as follows.
1369
1370 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1371
1372 OTA server can pass down this string, in addition to the package URL, to the
1373 system update client. System update client can then fetch individual ZIP
1374 entries (ZIP_STORED) directly at the given offset of the URL.
1375
1376 Args:
1377 metadata: The metadata dict for the package.
1378 input_file: The input ZIP filename that doesn't contain the package METADATA
1379 entry yet.
1380 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001381 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001382 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001383
Tao Baod2ce2ed2018-03-16 12:59:42 -07001384 def ComputeAllPropertyFiles(input_file, needed_property_files):
1385 # Write the current metadata entry with placeholders.
1386 with zipfile.ZipFile(input_file) as input_zip:
1387 for property_files in needed_property_files:
1388 metadata[property_files.name] = property_files.Compute(input_zip)
1389 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001390
Tao Baod2ce2ed2018-03-16 12:59:42 -07001391 if METADATA_NAME in namelist:
1392 common.ZipDelete(input_file, METADATA_NAME)
1393 output_zip = zipfile.ZipFile(input_file, 'a')
1394 WriteMetadata(metadata, output_zip)
1395 common.ZipClose(output_zip)
1396
1397 if OPTIONS.no_signing:
1398 return input_file
1399
Tao Bao491d7e22018-02-21 13:17:22 -08001400 prelim_signing = common.MakeTempFile(suffix='.zip')
1401 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001402 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001403
Tao Baod2ce2ed2018-03-16 12:59:42 -07001404 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1405 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1406 for property_files in needed_property_files:
1407 metadata[property_files.name] = property_files.Finalize(
1408 prelim_signing_zip, len(metadata[property_files.name]))
1409
1410 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1411 # entries, as well as padding the entry headers. We do a preliminary signing
1412 # (with an incomplete metadata entry) to allow that to happen. Then compute
1413 # the ZIP entry offsets, write back the final metadata and do the final
1414 # signing.
1415 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1416 try:
1417 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1418 except PropertyFiles.InsufficientSpaceException:
1419 # Even with the preliminary signing, the entry orders may change
1420 # dramatically, which leads to insufficiently reserved space during the
1421 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1422 # preliminary signing works, based on the already ordered ZIP entries, to
1423 # address the issue.
1424 prelim_signing = ComputeAllPropertyFiles(
1425 prelim_signing, needed_property_files)
1426 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001427
1428 # Replace the METADATA entry.
1429 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001430 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001431 WriteMetadata(metadata, output_zip)
1432 common.ZipClose(output_zip)
1433
1434 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001435 if OPTIONS.no_signing:
1436 output_file = prelim_signing
1437 else:
1438 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001439
1440 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001441 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001442 for property_files in needed_property_files:
1443 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001444
xunchang1cfe2512019-02-19 14:14:48 -08001445 # If requested, dump the metadata to a separate file.
1446 output_metadata_path = OPTIONS.output_metadata_path
1447 if output_metadata_path:
1448 WriteMetadata(metadata, output_metadata_path)
1449
Tao Baofe5b69a2018-03-02 09:47:43 -08001450
Tao Bao491d7e22018-02-21 13:17:22 -08001451def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001452 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1453 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001454
Tao Bao481bab82017-12-21 11:23:09 -08001455 target_api_version = target_info["recovery_api_version"]
1456 source_api_version = source_info["recovery_api_version"]
1457 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001458 logger.warning(
1459 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001460
Tao Bao481bab82017-12-21 11:23:09 -08001461 script = edify_generator.EdifyGenerator(
1462 source_api_version, target_info, fstab=source_info["fstab"])
1463
1464 if target_info.oem_props or source_info.oem_props:
1465 if not OPTIONS.oem_no_mount:
1466 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001467
Tao Baodf3a48b2018-01-10 16:30:43 -08001468 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001469
Tao Bao491d7e22018-02-21 13:17:22 -08001470 if not OPTIONS.no_signing:
1471 staging_file = common.MakeTempFile(suffix='.zip')
1472 else:
1473 staging_file = output_file
1474
1475 output_zip = zipfile.ZipFile(
1476 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1477
Geremy Condra36bd3652014-02-06 19:45:10 -08001478 device_specific = common.DeviceSpecificParams(
1479 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001480 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001481 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001482 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001483 output_zip=output_zip,
1484 script=script,
1485 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001486 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001487
Geremy Condra36bd3652014-02-06 19:45:10 -08001488 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001489 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001490 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001491 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001492 updating_boot = (not OPTIONS.two_step and
1493 (source_boot.data != target_boot.data))
1494
Geremy Condra36bd3652014-02-06 19:45:10 -08001495 target_recovery = common.GetBootableImage(
1496 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001497
Tao Baoe709b092018-02-07 12:40:00 -08001498 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1499 # shared blocks (i.e. some blocks will show up in multiple files' block
1500 # list). We can only allocate such shared blocks to the first "owner", and
1501 # disable imgdiff for all later occurrences.
1502 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1503 target_info.get('ext4_share_dup_blocks') == "true")
1504 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1505 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001506
1507 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1508 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001509 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001510 allow_shared_blocks,
1511 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001512
Tao Bao0582cb62017-12-21 11:47:01 -08001513 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001514 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001515 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001516
Tao Baof8acad12016-07-07 09:09:58 -07001517 # Check the first block of the source system partition for remount R/W only
1518 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001519 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001520 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001521 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1522 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1523 # b) the blocks listed in block map may not contain all the bytes for a given
1524 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001525 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001526 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1527 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001528 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001529 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001530 version=blockimgdiff_version,
1531 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001532
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001533 if HasVendorPartition(target_zip):
1534 if not HasVendorPartition(source_zip):
1535 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001536 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1537 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001538 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1539 "vendor", 4096, target_info)
1540 vendor_tgt = common.GetSparseImage(
1541 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1542 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001543
1544 # Check first block of vendor partition for remount R/W only if
1545 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001546 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001547 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001548 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001549 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001550 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001551 version=blockimgdiff_version,
1552 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001553 else:
1554 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001555
Tao Baobcd1d162017-08-26 13:10:26 -07001556 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001557 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001558
Tao Bao481bab82017-12-21 11:23:09 -08001559 # Assertions (e.g. device properties check).
1560 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001561 device_specific.IncrementalOTA_Assertions()
1562
1563 # Two-step incremental package strategy (in chronological order,
1564 # which is *not* the order in which the generated script has
1565 # things):
1566 #
1567 # if stage is not "2/3" or "3/3":
1568 # do verification on current system
1569 # write recovery image to boot partition
1570 # set stage to "2/3"
1571 # reboot to boot partition and restart recovery
1572 # else if stage is "2/3":
1573 # write recovery image to recovery partition
1574 # set stage to "3/3"
1575 # reboot to recovery partition and restart recovery
1576 # else:
1577 # (stage must be "3/3")
1578 # perform update:
1579 # patch system files, etc.
1580 # force full install of new boot image
1581 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001582 # complete script normally
1583 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001584
1585 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001586 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001587 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001588 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001589 assert fs.fs_type.upper() == "EMMC", \
1590 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001591 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001592 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1593 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001594if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001595""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001596
1597 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1598 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001599 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001600 script.WriteRawImage("/recovery", "recovery.img")
1601 script.AppendExtra("""
1602set_stage("%(bcb_dev)s", "3/3");
1603reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001604else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001605""" % bcb_dev)
1606
Tao Baod42e97e2016-11-30 12:11:57 -08001607 # Stage 1/3: (a) Verify the current system.
1608 script.Comment("Stage 1/3")
1609
Tao Bao6c55a8a2015-04-08 15:30:27 -07001610 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001611 script.Print("Source: {}".format(source_info.fingerprint))
1612 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001613
Geremy Condra36bd3652014-02-06 19:45:10 -08001614 script.Print("Verifying current system...")
1615
1616 device_specific.IncrementalOTA_VerifyBegin()
1617
Tao Bao481bab82017-12-21 11:23:09 -08001618 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001619
Tao Baod8d14be2016-02-04 14:26:02 -08001620 # Check the required cache size (i.e. stashed blocks).
1621 size = []
1622 if system_diff:
1623 size.append(system_diff.required_cache)
1624 if vendor_diff:
1625 size.append(vendor_diff.required_cache)
1626
Geremy Condra36bd3652014-02-06 19:45:10 -08001627 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001628 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001629 d = common.Difference(target_boot, source_boot)
1630 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001631 if d is None:
1632 include_full_boot = True
1633 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1634 else:
1635 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001636
Tao Bao32fcdab2018-10-12 10:30:39 -07001637 logger.info(
1638 "boot target: %d source: %d diff: %d", target_boot.size,
1639 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001640
Tao Bao51216552018-08-26 11:53:15 -07001641 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001642
Tao Bao51216552018-08-26 11:53:15 -07001643 script.PatchPartitionCheck(
1644 "{}:{}:{}:{}".format(
1645 boot_type, boot_device, target_boot.size, target_boot.sha1),
1646 "{}:{}:{}:{}".format(
1647 boot_type, boot_device, source_boot.size, source_boot.sha1))
1648
Tao Baod8d14be2016-02-04 14:26:02 -08001649 size.append(target_boot.size)
1650
1651 if size:
1652 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001653
1654 device_specific.IncrementalOTA_VerifyEnd()
1655
1656 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001657 # Stage 1/3: (b) Write recovery image to /boot.
1658 _WriteRecoveryImageToBoot(script, output_zip)
1659
Geremy Condra36bd3652014-02-06 19:45:10 -08001660 script.AppendExtra("""
1661set_stage("%(bcb_dev)s", "2/3");
1662reboot_now("%(bcb_dev)s", "");
1663else
1664""" % bcb_dev)
1665
Tao Baod42e97e2016-11-30 12:11:57 -08001666 # Stage 3/3: Make changes.
1667 script.Comment("Stage 3/3")
1668
Jesse Zhao75bcea02015-01-06 10:59:53 -08001669 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001670 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001671 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001672 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001673 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1674 if device_specific_diffs:
1675 assert all(isinstance(diff, common.BlockDifference)
1676 for diff in device_specific_diffs), \
1677 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1678 "BlockDifference objects"
1679 for diff in device_specific_diffs:
1680 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001681
Geremy Condra36bd3652014-02-06 19:45:10 -08001682 script.Comment("---- start making changes here ----")
1683
1684 device_specific.IncrementalOTA_InstallBegin()
1685
Yifan Hong10c530d2018-12-27 17:34:18 -08001686 block_diffs = [system_diff]
1687 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001688 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001689 block_diffs.append(vendor_diff)
1690 progress_dict["vendor"] = 0.1
1691 if device_specific_diffs:
1692 block_diffs += device_specific_diffs
1693
1694 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1695 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1696 raise RuntimeError(
1697 "can't generate incremental that disables dynamic partitions")
1698 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1699 info_dict=OPTIONS.target_info_dict,
1700 source_info_dict=OPTIONS.source_info_dict,
1701 block_diffs=block_diffs,
1702 progress_dict=progress_dict)
1703 dynamic_partitions_diff.WriteScript(
1704 script, output_zip, write_verify_script=OPTIONS.verify)
1705 else:
1706 for block_diff in block_diffs:
1707 block_diff.WriteScript(script, output_zip,
1708 progress=progress_dict.get(block_diff.partition),
1709 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001710
1711 if OPTIONS.two_step:
1712 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1713 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001714 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001715
1716 if not OPTIONS.two_step:
1717 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001718 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001719 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001720 script.Print("Installing boot image...")
1721 script.WriteRawImage("/boot", "boot.img")
1722 else:
1723 # Produce the boot image by applying a patch to the current
1724 # contents of the boot partition, and write it back to the
1725 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001726 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001727 script.Print("Patching boot image...")
1728 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001729 script.PatchPartition(
1730 '{}:{}:{}:{}'.format(
1731 boot_type, boot_device, target_boot.size, target_boot.sha1),
1732 '{}:{}:{}:{}'.format(
1733 boot_type, boot_device, source_boot.size, source_boot.sha1),
1734 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001735 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001736 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001737
1738 # Do device-specific installation (eg, write radio image).
1739 device_specific.IncrementalOTA_InstallEnd()
1740
1741 if OPTIONS.extra_script is not None:
1742 script.AppendExtra(OPTIONS.extra_script)
1743
Doug Zongker922206e2014-03-04 13:16:24 -08001744 if OPTIONS.wipe_user_data:
1745 script.Print("Erasing user data...")
1746 script.FormatPartition("/data")
1747
Geremy Condra36bd3652014-02-06 19:45:10 -08001748 if OPTIONS.two_step:
1749 script.AppendExtra("""
1750set_stage("%(bcb_dev)s", "");
1751endif;
1752endif;
1753""" % bcb_dev)
1754
1755 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001756 # For downgrade OTAs, we prefer to use the update-binary in the source
1757 # build that is actually newer than the one in the target build.
1758 if OPTIONS.downgrade:
1759 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1760 else:
1761 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001762 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001763
1764 # We haven't written the metadata entry yet, which will be handled in
1765 # FinalizeMetadata().
1766 common.ZipClose(output_zip)
1767
1768 # Sign the generated zip package unless no_signing is specified.
1769 needed_property_files = (
1770 NonAbOtaPropertyFiles(),
1771 )
1772 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001773
Doug Zongker32b527d2014-03-04 10:03:02 -08001774
Tao Bao15a146a2018-02-21 16:06:59 -08001775def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001776 """Returns a target-files.zip file for generating secondary payload.
1777
1778 Although the original target-files.zip already contains secondary slot
1779 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1780 ones without _other suffix. Note that we cannot instead modify the names in
1781 META/ab_partitions.txt, because there are no matching partitions on device.
1782
1783 For the partitions that don't have secondary images, the ones for primary
1784 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1785 bootloader images in the inactive slot.
1786
1787 Args:
1788 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001789 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001790
1791 Returns:
1792 The filename of the target-files.zip for generating secondary payload.
1793 """
1794 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1795 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1796
Tao Baodba59ee2018-01-09 13:21:02 -08001797 with zipfile.ZipFile(input_file, 'r') as input_zip:
1798 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001799 namelist = input_zip.namelist()
1800
1801 # Additionally unzip 'RADIO/*' if exists.
1802 unzip_pattern = UNZIP_PATTERN[:]
1803 if any([entry.startswith('RADIO/') for entry in namelist]):
1804 unzip_pattern.append('RADIO/*')
1805 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001806
1807 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001808 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1809 if info.filename == 'IMAGES/system_other.img':
1810 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1811
1812 # Primary images and friends need to be skipped explicitly.
1813 elif info.filename in ('IMAGES/system.img',
1814 'IMAGES/system.map'):
1815 pass
1816
Tao Bao15a146a2018-02-21 16:06:59 -08001817 # Skip copying the postinstall config if requested.
1818 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1819 pass
1820
Tao Bao12489802018-07-12 14:47:38 -07001821 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001822 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1823
Tao Baof7140c02018-01-30 17:09:24 -08001824 common.ZipClose(target_zip)
1825
1826 return target_file
1827
1828
Tao Bao15a146a2018-02-21 16:06:59 -08001829def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1830 """Returns a target-files.zip that's not containing postinstall_config.txt.
1831
1832 This allows brillo_update_payload script to skip writing all the postinstall
1833 hooks in the generated payload. The input target-files.zip file will be
1834 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1835 contain the postinstall_config.txt entry, the input file will be returned.
1836
1837 Args:
1838 input_file: The input target-files.zip filename.
1839
1840 Returns:
1841 The filename of target-files.zip that doesn't contain postinstall config.
1842 """
1843 # We should only make a copy if postinstall_config entry exists.
1844 with zipfile.ZipFile(input_file, 'r') as input_zip:
1845 if POSTINSTALL_CONFIG not in input_zip.namelist():
1846 return input_file
1847
1848 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1849 shutil.copyfile(input_file, target_file)
1850 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1851 return target_file
1852
1853
Yifan Hong50e79542018-11-08 17:44:12 -08001854def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
1855 super_block_devices):
1856 """Returns a target-files.zip for retrofitting dynamic partitions.
1857
1858 This allows brillo_update_payload to generate an OTA based on the exact
1859 bits on the block devices. Postinstall is disabled.
1860
1861 Args:
1862 input_file: The input target-files.zip filename.
1863 super_block_devices: The list of super block devices
1864
1865 Returns:
1866 The filename of target-files.zip with *.img replaced with super_*.img for
1867 each block device in super_block_devices.
1868 """
1869 assert super_block_devices, "No super_block_devices are specified."
1870
1871 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001872 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001873
1874 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1875 shutil.copyfile(input_file, target_file)
1876
1877 with zipfile.ZipFile(input_file, 'r') as input_zip:
1878 namelist = input_zip.namelist()
1879
1880 # Always skip postinstall for a retrofit update.
1881 to_delete = [POSTINSTALL_CONFIG]
1882
1883 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1884 # is a regular update on devices without dynamic partitions support.
1885 to_delete += [DYNAMIC_PARTITION_INFO]
1886
Tao Bao03fecb62018-11-28 10:59:23 -08001887 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001888 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001889 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001890
1891 common.ZipDelete(target_file, to_delete)
1892
1893 input_tmp = common.UnzipTemp(input_file, SUPER_SPLIT_PATTERN)
1894 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1895
1896 # Write super_{foo}.img as {foo}.img.
1897 for src, dst in replace.items():
1898 assert src in namelist, \
1899 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1900 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1901 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1902
1903 common.ZipClose(target_zip)
1904
1905 return target_file
1906
1907
Tao Baoc098e9e2016-01-07 13:03:56 -08001908def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1909 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001910 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001911 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001912 if not OPTIONS.no_signing:
1913 staging_file = common.MakeTempFile(suffix='.zip')
1914 else:
1915 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001916 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001917 compression=zipfile.ZIP_DEFLATED)
1918
Tao Bao481bab82017-12-21 11:23:09 -08001919 if source_file is not None:
1920 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1921 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1922 else:
1923 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1924 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001925
Tao Bao481bab82017-12-21 11:23:09 -08001926 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001927 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001928
Yifan Hong50e79542018-11-08 17:44:12 -08001929 if OPTIONS.retrofit_dynamic_partitions:
1930 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
1931 target_file, target_info.get("super_block_devices").strip().split())
1932 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001933 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1934
Tao Bao40b18822018-01-30 18:19:04 -08001935 # Generate payload.
1936 payload = Payload()
1937
1938 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001939 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001940 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001941 else:
1942 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001943 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001944
Tao Bao40b18822018-01-30 18:19:04 -08001945 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001946
Tao Bao40b18822018-01-30 18:19:04 -08001947 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001948 payload_signer = PayloadSigner()
1949 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001950
Tao Bao40b18822018-01-30 18:19:04 -08001951 # Write the payload into output zip.
1952 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001953
Tao Baof7140c02018-01-30 17:09:24 -08001954 # Generate and include the secondary payload that installs secondary images
1955 # (e.g. system_other.img).
1956 if OPTIONS.include_secondary:
1957 # We always include a full payload for the secondary slot, even when
1958 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001959 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1960 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001961 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001962 secondary_payload.Generate(secondary_target_file,
1963 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001964 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001965 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001966
Tianjie Xucfa86222016-03-07 16:31:19 -08001967 # If dm-verity is supported for the device, copy contents of care_map
1968 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001969 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001970 if (target_info.get("verity") == "true" or
1971 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001972 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1973 "META/" + x in target_zip.namelist()]
1974
1975 # Adds care_map if either the protobuf format or the plain text one exists.
1976 if care_map_list:
1977 care_map_name = care_map_list[0]
1978 care_map_data = target_zip.read("META/" + care_map_name)
1979 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001980 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001981 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001982 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001983 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001984 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001985
Tao Baobcd1d162017-08-26 13:10:26 -07001986 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001987 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001988
Tao Bao21803d32017-04-19 10:16:09 -07001989 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001990
Tao Baofe5b69a2018-03-02 09:47:43 -08001991 # We haven't written the metadata entry yet, which will be handled in
1992 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001993 common.ZipClose(output_zip)
1994
Tao Bao85f16982018-03-08 16:28:33 -08001995 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1996 # all the info of the latter. However, system updaters and OTA servers need to
1997 # take time to switch to the new flag. We keep both of the flags for
1998 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001999 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002000 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002001 StreamingPropertyFiles(),
2002 )
2003 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002004
Tao Baoc098e9e2016-01-07 13:03:56 -08002005
Doug Zongkereef39442009-04-02 12:14:19 -07002006def main(argv):
2007
2008 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002009 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002010 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002011 elif o in ("-i", "--incremental_from"):
2012 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002013 elif o == "--full_radio":
2014 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002015 elif o == "--full_bootloader":
2016 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002017 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002018 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002019 elif o == "--downgrade":
2020 OPTIONS.downgrade = True
2021 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002022 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002023 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002024 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002025 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002026 elif o == "--oem_no_mount":
2027 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002028 elif o in ("-e", "--extra_script"):
2029 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002030 elif o in ("-t", "--worker_threads"):
2031 if a.isdigit():
2032 OPTIONS.worker_threads = int(a)
2033 else:
2034 raise ValueError("Cannot parse value %r for option %r - only "
2035 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002036 elif o in ("-2", "--two_step"):
2037 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002038 elif o == "--include_secondary":
2039 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002040 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002041 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002042 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002043 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002044 elif o == "--block":
2045 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002046 elif o in ("-b", "--binary"):
2047 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002048 elif o == "--stash_threshold":
2049 try:
2050 OPTIONS.stash_threshold = float(a)
2051 except ValueError:
2052 raise ValueError("Cannot parse value %r for option %r - expecting "
2053 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002054 elif o == "--log_diff":
2055 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002056 elif o == "--payload_signer":
2057 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002058 elif o == "--payload_signer_args":
2059 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002060 elif o == "--extracted_input_target_files":
2061 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002062 elif o == "--skip_postinstall":
2063 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002064 elif o == "--retrofit_dynamic_partitions":
2065 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002066 elif o == "--skip_compatibility_check":
2067 OPTIONS.skip_compatibility_check = True
xunchang1cfe2512019-02-19 14:14:48 -08002068 elif o == "--output_metadata_path":
2069 OPTIONS.output_metadata_path = a
Doug Zongkereef39442009-04-02 12:14:19 -07002070 else:
2071 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002072 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002073
2074 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002075 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002076 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002077 "package_key=",
2078 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002079 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002080 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002081 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002082 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002083 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002084 "extra_script=",
2085 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002086 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002087 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002088 "no_signing",
2089 "block",
2090 "binary=",
2091 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002092 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002093 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002094 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002095 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002096 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002097 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002098 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002099 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002100 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002101 "skip_compatibility_check",
xunchang1cfe2512019-02-19 14:14:48 -08002102 "output_metadata_path=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002103 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002104
2105 if len(args) != 2:
2106 common.Usage(__doc__)
2107 sys.exit(1)
2108
Tao Bao32fcdab2018-10-12 10:30:39 -07002109 common.InitLogging()
2110
Tao Bao5d182562016-02-23 11:38:39 -08002111 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002112 # We should only allow downgrading incrementals (as opposed to full).
2113 # Otherwise the device may go back from arbitrary build with this full
2114 # OTA package.
2115 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002116 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002117
Tao Bao2db13852018-01-08 22:28:57 -08002118 # Load the build info dicts from the zip directly or the extracted input
2119 # directory. We don't need to unzip the entire target-files zips, because they
2120 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2121 # When loading the info dicts, we don't need to provide the second parameter
2122 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2123 # some properties with their actual paths, such as 'selinux_fc',
2124 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002125 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002126 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002127 else:
Tao Bao2db13852018-01-08 22:28:57 -08002128 with zipfile.ZipFile(args[0], 'r') as input_zip:
2129 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002130
Tao Bao32fcdab2018-10-12 10:30:39 -07002131 logger.info("--- target info ---")
2132 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002133
2134 # Load the source build dict if applicable.
2135 if OPTIONS.incremental_source is not None:
2136 OPTIONS.target_info_dict = OPTIONS.info_dict
2137 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2138 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2139
Tao Bao32fcdab2018-10-12 10:30:39 -07002140 logger.info("--- source info ---")
2141 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002142
2143 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002144 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2145
Yifan Hong50e79542018-11-08 17:44:12 -08002146 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002147 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002148 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002149 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2150 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002151 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2152 raise common.ExternalError(
2153 "Expect to generate incremental OTA for retrofitting dynamic "
2154 "partitions, but dynamic_partition_retrofit is not set in target "
2155 "build.")
2156 logger.info("Implicitly generating retrofit incremental OTA.")
2157 OPTIONS.retrofit_dynamic_partitions = True
2158
2159 # Skip postinstall for retrofitting dynamic partitions.
2160 if OPTIONS.retrofit_dynamic_partitions:
2161 OPTIONS.skip_postinstall = True
2162
Tao Baoc098e9e2016-01-07 13:03:56 -08002163 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2164
Christian Oderf63e2cd2017-05-01 22:30:15 +02002165 # Use the default key to sign the package if not specified with package_key.
2166 # package_keys are needed on ab_updates, so always define them if an
2167 # ab_update is getting created.
2168 if not OPTIONS.no_signing or ab_update:
2169 if OPTIONS.package_key is None:
2170 OPTIONS.package_key = OPTIONS.info_dict.get(
2171 "default_system_dev_certificate",
2172 "build/target/product/security/testkey")
2173 # Get signing keys
2174 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2175
Tao Baoc098e9e2016-01-07 13:03:56 -08002176 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002177 WriteABOTAPackageWithBrilloScript(
2178 target_file=args[0],
2179 output_file=args[1],
2180 source_file=OPTIONS.incremental_source)
2181
Tao Bao32fcdab2018-10-12 10:30:39 -07002182 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002183 return
2184
Tao Bao2db13852018-01-08 22:28:57 -08002185 # Sanity check the loaded info dicts first.
2186 if OPTIONS.info_dict.get("no_recovery") == "true":
2187 raise common.ExternalError(
2188 "--- target build has specified no recovery ---")
2189
2190 # Non-A/B OTAs rely on /cache partition to store temporary files.
2191 cache_size = OPTIONS.info_dict.get("cache_size")
2192 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002193 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002194 OPTIONS.cache_size = cache_size
2195
Doug Zongker1c390a22009-05-14 19:06:36 -07002196 if OPTIONS.extra_script is not None:
2197 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2198
Dan Willemsencea5cd22017-03-21 14:44:27 -07002199 if OPTIONS.extracted_input is not None:
2200 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002201 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002202 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002203 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002204 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002205
Tao Bao2db13852018-01-08 22:28:57 -08002206 # If the caller explicitly specified the device-specific extensions path via
2207 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2208 # is present in the target target_files. Otherwise, take the path of the file
2209 # from 'tool_extensions' in the info dict and look for that in the local
2210 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002211 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002212 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2213 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002214 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002215 OPTIONS.device_specific = from_input
2216 else:
Tao Bao2db13852018-01-08 22:28:57 -08002217 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002218
Doug Zongker37974732010-09-16 17:44:38 -07002219 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002220 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002221
Tao Bao767e3ac2015-11-10 12:19:19 -08002222 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002223 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002224 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002225 WriteFullOTAPackage(
2226 input_zip,
2227 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002228
Tao Bao32b80dc2018-01-08 22:50:47 -08002229 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002230 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002231 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002232 OPTIONS.source_tmp = common.UnzipTemp(
2233 OPTIONS.incremental_source, UNZIP_PATTERN)
2234 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2235 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002236 WriteBlockIncrementalOTAPackage(
2237 input_zip,
2238 source_zip,
2239 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002240
2241 if OPTIONS.log_diff:
2242 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002243 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002244 target_files_diff.recursiveDiff(
2245 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002246
Tao Bao32fcdab2018-10-12 10:30:39 -07002247 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002248
2249
2250if __name__ == '__main__':
2251 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002252 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002253 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002254 except common.ExternalError:
2255 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002256 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002257 finally:
2258 common.Cleanup()