blob: 82bcdce67dc4eadea89c282265f8acc0233ffb2a [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
Tao Bao30df8b42018-04-23 15:32:53 -070077Non-A/B OTA specific options
78
79 -b (--binary) <file>
80 Use the given binary as the update-binary in the output package, instead
81 of the binary in the build's target_files. Use for development only.
82
83 --block
84 Generate a block-based OTA for non-A/B device. We have deprecated the
85 support for file-based OTA since O. Block-based OTA will be used by
86 default for all non-A/B devices. Keeping this flag here to not break
87 existing callers.
88
89 -e (--extra_script) <file>
90 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070091
leozwangaa6c1a12015-08-14 10:57:58 -070092 --full_bootloader
93 Similar to --full_radio. When generating an incremental OTA, always
94 include a full copy of bootloader image.
95
Tao Bao30df8b42018-04-23 15:32:53 -070096 --full_radio
97 When generating an incremental OTA, always include a full copy of radio
98 image. This option is only meaningful when -i is specified, because a full
99 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -0700100
Tao Bao30df8b42018-04-23 15:32:53 -0700101 --log_diff <file>
102 Generate a log file that shows the differences in the source and target
103 builds for an incremental package. This option is only meaningful when -i
104 is specified.
105
106 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800107 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800108 properties on the OEM partition of the intended device. Multiple expected
109 values can be used by providing multiple files. Only the first dict will
110 be used to compute fingerprint, while the rest will be used to assert
111 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800112
Tao Bao8608cde2016-02-25 19:49:55 -0800113 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700114 For devices with OEM-specific properties but without an OEM partition, do
115 not mount the OEM partition in the updater-script. This should be very
116 rarely used, since it's expected to have a dedicated OEM partition for
117 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800118
Tao Bao30df8b42018-04-23 15:32:53 -0700119 --stash_threshold <float>
120 Specify the threshold that will be used to compute the maximum allowed
121 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700122
Tao Bao30df8b42018-04-23 15:32:53 -0700123 -t (--worker_threads) <int>
124 Specify the number of worker-threads that will be used when generating
125 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800126
Tao Bao30df8b42018-04-23 15:32:53 -0700127 --verify
128 Verify the checksums of the updated system and vendor (if any) partitions.
129 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700130
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800131 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700132 Generate a 'two-step' OTA package, where recovery is updated first, so
133 that any changes made to the system partition are done using the new
134 recovery (new kernel, etc.).
135
136A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800137
Tao Baof7140c02018-01-30 17:09:24 -0800138 --include_secondary
139 Additionally include the payload for secondary slot images (default:
140 False). Only meaningful when generating A/B OTAs.
141
142 By default, an A/B OTA package doesn't contain the images for the
143 secondary slot (e.g. system_other.img). Specifying this flag allows
144 generating a separate payload that will install secondary slot images.
145
146 Such a package needs to be applied in a two-stage manner, with a reboot
147 in-between. During the first stage, the updater applies the primary
148 payload only. Upon finishing, it reboots the device into the newly updated
149 slot. It then continues to install the secondary payload to the inactive
150 slot, but without switching the active slot at the end (needs the matching
151 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
152
153 Due to the special install procedure, the secondary payload will be always
154 generated as a full payload.
155
Tao Baodea0f8b2016-06-20 17:55:06 -0700156 --payload_signer <signer>
157 Specify the signer when signing the payload and metadata for A/B OTAs.
158 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
159 with the package private key. If the private key cannot be accessed
160 directly, a payload signer that knows how to do that should be specified.
161 The signer will be supplied with "-inkey <path_to_key>",
162 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700163
164 --payload_signer_args <args>
165 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800166
167 --skip_postinstall
168 Skip the postinstall hooks when generating an A/B OTA package (default:
169 False). Note that this discards ALL the hooks, including non-optional
170 ones. Should only be used if caller knows it's safe to do so (e.g. all the
171 postinstall work is to dexopt apps and a data wipe will happen immediately
172 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700173"""
174
Tao Bao89fbb0f2017-01-10 10:47:58 -0800175from __future__ import print_function
176
Tao Bao32fcdab2018-10-12 10:30:39 -0700177import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700178import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800179import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700180import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800181import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800182import struct
Tao Bao481bab82017-12-21 11:23:09 -0800183import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700184import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700185import zipfile
186
187import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700188import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700189import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700190
Tao Bao481bab82017-12-21 11:23:09 -0800191if sys.hexversion < 0x02070000:
192 print("Python 2.7 or newer is required.", file=sys.stderr)
193 sys.exit(1)
194
Tao Bao32fcdab2018-10-12 10:30:39 -0700195logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800196
Doug Zongkereef39442009-04-02 12:14:19 -0700197OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700198OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700199OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700200OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700201OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700202OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800203OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700204OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700205OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
206if OPTIONS.worker_threads == 0:
207 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800208OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800209OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900210OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800211OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800212OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700213OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800214OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700215OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700216OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700217# Stash size cannot exceed cache_size * threshold.
218OPTIONS.cache_size = None
219OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800220OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700221OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700222OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700223OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200224OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800225OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800226OPTIONS.retrofit_dynamic_partitions = False
xunchangabfa2652019-02-19 16:27:10 -0800227OPTIONS.skip_compatibility_check = False
Tao Bao15a146a2018-02-21 16:06:59 -0800228
Tao Bao8dcf7382015-05-21 14:09:49 -0700229
Tao Bao2dd1c482017-02-03 16:49:39 -0800230METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800231POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800232DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800233UNZIP_PATTERN = ['IMAGES/*', 'META/*']
Yifan Hong50e79542018-11-08 17:44:12 -0800234SUPER_SPLIT_PATTERN = ['OTA/super_*.img']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800235
Tao Bao2dd1c482017-02-03 16:49:39 -0800236
Tao Bao481bab82017-12-21 11:23:09 -0800237class BuildInfo(object):
238 """A class that holds the information for a given build.
239
240 This class wraps up the property querying for a given source or target build.
241 It abstracts away the logic of handling OEM-specific properties, and caches
242 the commonly used properties such as fingerprint.
243
244 There are two types of info dicts: a) build-time info dict, which is generated
245 at build time (i.e. included in a target_files zip); b) OEM info dict that is
246 specified at package generation time (via command line argument
247 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
248 having "oem_fingerprint_properties" in build-time info dict), all the queries
249 would be answered based on build-time info dict only. Otherwise if using
250 OEM-specific properties, some of them will be calculated from two info dicts.
251
252 Users can query properties similarly as using a dict() (e.g. info['fstab']),
253 or to query build properties via GetBuildProp() or GetVendorBuildProp().
254
255 Attributes:
256 info_dict: The build-time info dict.
257 is_ab: Whether it's a build that uses A/B OTA.
258 oem_dicts: A list of OEM dicts.
259 oem_props: A list of OEM properties that should be read from OEM dicts; None
260 if the build doesn't use any OEM-specific property.
261 fingerprint: The fingerprint of the build, which would be calculated based
262 on OEM properties if applicable.
263 device: The device name, which could come from OEM dicts if applicable.
264 """
265
Steven Laver9e73e822019-01-29 20:20:08 -0800266 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
267 "ro.product.manufacturer", "ro.product.model",
268 "ro.product.name"]
269 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "product_services",
270 "odm", "vendor", "system"]
271
Tao Bao481bab82017-12-21 11:23:09 -0800272 def __init__(self, info_dict, oem_dicts):
273 """Initializes a BuildInfo instance with the given dicts.
274
Tao Bao667c7532018-07-06 10:13:59 -0700275 Note that it only wraps up the given dicts, without making copies.
276
Tao Bao481bab82017-12-21 11:23:09 -0800277 Arguments:
278 info_dict: The build-time info dict.
279 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
280 that it always uses the first dict to calculate the fingerprint or the
281 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700282 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800283 """
284 self.info_dict = info_dict
285 self.oem_dicts = oem_dicts
286
287 self._is_ab = info_dict.get("ab_update") == "true"
288 self._oem_props = info_dict.get("oem_fingerprint_properties")
289
290 if self._oem_props:
291 assert oem_dicts, "OEM source required for this build"
292
293 # These two should be computed only after setting self._oem_props.
294 self._device = self.GetOemProperty("ro.product.device")
295 self._fingerprint = self.CalculateFingerprint()
296
297 @property
298 def is_ab(self):
299 return self._is_ab
300
301 @property
302 def device(self):
303 return self._device
304
305 @property
306 def fingerprint(self):
307 return self._fingerprint
308
309 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700310 def vendor_fingerprint(self):
311 if "vendor.build.prop" not in self.info_dict:
312 return None
313 vendor_build_prop = self.info_dict["vendor.build.prop"]
314 if "ro.vendor.build.fingerprint" in vendor_build_prop:
315 return vendor_build_prop["ro.vendor.build.fingerprint"]
316 if "ro.vendor.build.thumbprint" in vendor_build_prop:
317 return vendor_build_prop["ro.vendor.build.thumbprint"]
318 return None
319
320 @property
Tao Bao481bab82017-12-21 11:23:09 -0800321 def oem_props(self):
322 return self._oem_props
323
324 def __getitem__(self, key):
325 return self.info_dict[key]
326
Tao Bao667c7532018-07-06 10:13:59 -0700327 def __setitem__(self, key, value):
328 self.info_dict[key] = value
329
Tao Bao481bab82017-12-21 11:23:09 -0800330 def get(self, key, default=None):
331 return self.info_dict.get(key, default)
332
Tao Bao667c7532018-07-06 10:13:59 -0700333 def items(self):
334 return self.info_dict.items()
335
Tao Bao481bab82017-12-21 11:23:09 -0800336 def GetBuildProp(self, prop):
337 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800338 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
339 return self._ResolveRoProductBuildProp(prop)
340
Tao Bao481bab82017-12-21 11:23:09 -0800341 try:
342 return self.info_dict.get("build.prop", {})[prop]
343 except KeyError:
344 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
345
Steven Laver9e73e822019-01-29 20:20:08 -0800346 def _ResolveRoProductBuildProp(self, prop):
347 """Resolves the inquired ro.product.* build property"""
348 prop_val = self.info_dict.get("build.prop", {}).get(prop)
349 if prop_val:
350 return prop_val
351
352 source_order_val = self.info_dict.get("build.prop", {}).get(
353 "ro.product.property_source_order")
354 if source_order_val:
355 source_order = source_order_val.split(",")
356 else:
357 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
358
359 # Check that all sources in ro.product.property_source_order are valid
360 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
361 for x in source_order]):
362 raise common.ExternalError(
363 "Invalid ro.product.property_source_order '{}'".format(source_order))
364
365 for source in source_order:
366 source_prop = prop.replace("ro.product", "ro.product.{}".format(source),
367 1)
368 prop_val = self.info_dict.get("{}.build.prop".format(source), {}).get(
369 source_prop)
370 if prop_val:
371 return prop_val
372
373 raise common.ExternalError("couldn't resolve {}".format(prop))
374
Tao Bao481bab82017-12-21 11:23:09 -0800375 def GetVendorBuildProp(self, prop):
376 """Returns the inquired vendor build property."""
377 try:
378 return self.info_dict.get("vendor.build.prop", {})[prop]
379 except KeyError:
380 raise common.ExternalError(
381 "couldn't find %s in vendor.build.prop" % (prop,))
382
383 def GetOemProperty(self, key):
384 if self.oem_props is not None and key in self.oem_props:
385 return self.oem_dicts[0][key]
386 return self.GetBuildProp(key)
387
388 def CalculateFingerprint(self):
389 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800390 try:
391 return self.GetBuildProp("ro.build.fingerprint")
392 except common.ExternalError:
393 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
394 self.GetBuildProp("ro.product.brand"),
395 self.GetBuildProp("ro.product.name"),
396 self.GetBuildProp("ro.product.device"),
397 self.GetBuildProp("ro.build.version.release"),
398 self.GetBuildProp("ro.build.id"),
399 self.GetBuildProp("ro.build.version.incremental"),
400 self.GetBuildProp("ro.build.type"),
401 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800402 return "%s/%s/%s:%s" % (
403 self.GetOemProperty("ro.product.brand"),
404 self.GetOemProperty("ro.product.name"),
405 self.GetOemProperty("ro.product.device"),
406 self.GetBuildProp("ro.build.thumbprint"))
407
408 def WriteMountOemScript(self, script):
409 assert self.oem_props is not None
410 recovery_mount_options = self.info_dict.get("recovery_mount_options")
411 script.Mount("/oem", recovery_mount_options)
412
413 def WriteDeviceAssertions(self, script, oem_no_mount):
414 # Read the property directly if not using OEM properties.
415 if not self.oem_props:
416 script.AssertDevice(self.device)
417 return
418
419 # Otherwise assert OEM properties.
420 if not self.oem_dicts:
421 raise common.ExternalError(
422 "No OEM file provided to answer expected assertions")
423
424 for prop in self.oem_props.split():
425 values = []
426 for oem_dict in self.oem_dicts:
427 if prop in oem_dict:
428 values.append(oem_dict[prop])
429 if not values:
430 raise common.ExternalError(
431 "The OEM file is missing the property %s" % (prop,))
432 script.AssertOemProperty(prop, values, oem_no_mount)
433
434
Tao Baofabe0832018-01-17 15:52:28 -0800435class PayloadSigner(object):
436 """A class that wraps the payload signing works.
437
438 When generating a Payload, hashes of the payload and metadata files will be
439 signed with the device key, either by calling an external payload signer or
440 by calling openssl with the package key. This class provides a unified
441 interface, so that callers can just call PayloadSigner.Sign().
442
443 If an external payload signer has been specified (OPTIONS.payload_signer), it
444 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
445 that the signing key should be provided as part of the payload_signer_args.
446 Otherwise without an external signer, it uses the package key
447 (OPTIONS.package_key) and calls openssl for the signing works.
448 """
449
450 def __init__(self):
451 if OPTIONS.payload_signer is None:
452 # Prepare the payload signing key.
453 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
454 pw = OPTIONS.key_passwords[OPTIONS.package_key]
455
456 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
457 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
458 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
459 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700460 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800461
462 self.signer = "openssl"
463 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
464 "-pkeyopt", "digest:sha256"]
465 else:
466 self.signer = OPTIONS.payload_signer
467 self.signer_args = OPTIONS.payload_signer_args
468
469 def Sign(self, in_file):
470 """Signs the given input file. Returns the output filename."""
471 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
472 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700473 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800474 return out_file
475
476
Tao Bao40b18822018-01-30 18:19:04 -0800477class Payload(object):
478 """Manages the creation and the signing of an A/B OTA Payload."""
479
480 PAYLOAD_BIN = 'payload.bin'
481 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800482 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
483 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800484
Tao Bao667ff572018-02-10 00:02:40 -0800485 def __init__(self, secondary=False):
486 """Initializes a Payload instance.
487
488 Args:
489 secondary: Whether it's generating a secondary payload (default: False).
490 """
Tao Bao40b18822018-01-30 18:19:04 -0800491 self.payload_file = None
492 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800493 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800494
495 def Generate(self, target_file, source_file=None, additional_args=None):
496 """Generates a payload from the given target-files zip(s).
497
498 Args:
499 target_file: The filename of the target build target-files zip.
500 source_file: The filename of the source build target-files zip; or None if
501 generating a full OTA.
502 additional_args: A list of additional args that should be passed to
503 brillo_update_payload script; or None.
504 """
505 if additional_args is None:
506 additional_args = []
507
508 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
509 cmd = ["brillo_update_payload", "generate",
510 "--payload", payload_file,
511 "--target_image", target_file]
512 if source_file is not None:
513 cmd.extend(["--source_image", source_file])
514 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700515 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800516
517 self.payload_file = payload_file
518 self.payload_properties = None
519
520 def Sign(self, payload_signer):
521 """Generates and signs the hashes of the payload and metadata.
522
523 Args:
524 payload_signer: A PayloadSigner() instance that serves the signing work.
525
526 Raises:
527 AssertionError: On any failure when calling brillo_update_payload script.
528 """
529 assert isinstance(payload_signer, PayloadSigner)
530
531 # 1. Generate hashes of the payload and metadata files.
532 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
533 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
534 cmd = ["brillo_update_payload", "hash",
535 "--unsigned_payload", self.payload_file,
536 "--signature_size", "256",
537 "--metadata_hash_file", metadata_sig_file,
538 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700539 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800540
541 # 2. Sign the hashes.
542 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
543 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
544
545 # 3. Insert the signatures back into the payload file.
546 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
547 suffix=".bin")
548 cmd = ["brillo_update_payload", "sign",
549 "--unsigned_payload", self.payload_file,
550 "--payload", signed_payload_file,
551 "--signature_size", "256",
552 "--metadata_signature_file", signed_metadata_sig_file,
553 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700554 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800555
556 # 4. Dump the signed payload properties.
557 properties_file = common.MakeTempFile(prefix="payload-properties-",
558 suffix=".txt")
559 cmd = ["brillo_update_payload", "properties",
560 "--payload", signed_payload_file,
561 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700562 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800563
Tao Bao667ff572018-02-10 00:02:40 -0800564 if self.secondary:
565 with open(properties_file, "a") as f:
566 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
567
Tao Bao40b18822018-01-30 18:19:04 -0800568 if OPTIONS.wipe_user_data:
569 with open(properties_file, "a") as f:
570 f.write("POWERWASH=1\n")
571
572 self.payload_file = signed_payload_file
573 self.payload_properties = properties_file
574
Tao Bao667ff572018-02-10 00:02:40 -0800575 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800576 """Writes the payload to the given zip.
577
578 Args:
579 output_zip: The output ZipFile instance.
580 """
581 assert self.payload_file is not None
582 assert self.payload_properties is not None
583
Tao Bao667ff572018-02-10 00:02:40 -0800584 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800585 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
586 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
587 else:
588 payload_arcname = Payload.PAYLOAD_BIN
589 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
590
Tao Bao40b18822018-01-30 18:19:04 -0800591 # Add the signed payload file and properties into the zip. In order to
592 # support streaming, we pack them as ZIP_STORED. So these entries can be
593 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800594 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800595 compress_type=zipfile.ZIP_STORED)
596 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800597 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800598 compress_type=zipfile.ZIP_STORED)
599
600
Doug Zongkereef39442009-04-02 12:14:19 -0700601def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200602 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700603
Doug Zongker951495f2009-08-14 12:44:19 -0700604 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
605 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700606
607
Tao Bao481bab82017-12-21 11:23:09 -0800608def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800609 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800610 if not oem_source:
611 return None
612
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800613 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800614 for oem_file in oem_source:
615 with open(oem_file) as fp:
616 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800617 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700618
Doug Zongkereef39442009-04-02 12:14:19 -0700619
Tao Baod42e97e2016-11-30 12:11:57 -0800620def _WriteRecoveryImageToBoot(script, output_zip):
621 """Find and write recovery image to /boot in two-step OTA.
622
623 In two-step OTAs, we write recovery image to /boot as the first step so that
624 we can reboot to there and install a new recovery image to /recovery.
625 A special "recovery-two-step.img" will be preferred, which encodes the correct
626 path of "/boot". Otherwise the device may show "device is corrupt" message
627 when booting into /boot.
628
629 Fall back to using the regular recovery.img if the two-step recovery image
630 doesn't exist. Note that rebuilding the special image at this point may be
631 infeasible, because we don't have the desired boot signer and keys when
632 calling ota_from_target_files.py.
633 """
634
635 recovery_two_step_img_name = "recovery-two-step.img"
636 recovery_two_step_img_path = os.path.join(
637 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
638 if os.path.exists(recovery_two_step_img_path):
639 recovery_two_step_img = common.GetBootableImage(
640 recovery_two_step_img_name, recovery_two_step_img_name,
641 OPTIONS.input_tmp, "RECOVERY")
642 common.ZipWriteStr(
643 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700644 logger.info(
645 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800646 script.WriteRawImage("/boot", recovery_two_step_img_name)
647 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700648 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800649 # The "recovery.img" entry has been written into package earlier.
650 script.WriteRawImage("/boot", "recovery.img")
651
652
Doug Zongkerc9253822014-02-04 12:17:58 -0800653def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700654 namelist = [name for name in target_files_zip.namelist()]
655 return ("SYSTEM/recovery-from-boot.p" in namelist or
656 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700657
Tao Bao457cbf62017-03-06 09:56:01 -0800658
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700659def HasVendorPartition(target_files_zip):
660 try:
661 target_files_zip.getinfo("VENDOR/")
662 return True
663 except KeyError:
664 return False
665
Tao Bao457cbf62017-03-06 09:56:01 -0800666
Tao Bao481bab82017-12-21 11:23:09 -0800667def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700668 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800669 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700670
671
Tao Bao481bab82017-12-21 11:23:09 -0800672def WriteFingerprintAssertion(script, target_info, source_info):
673 source_oem_props = source_info.oem_props
674 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700675
Tao Bao481bab82017-12-21 11:23:09 -0800676 if source_oem_props is None and target_oem_props is None:
677 script.AssertSomeFingerprint(
678 source_info.fingerprint, target_info.fingerprint)
679 elif source_oem_props is not None and target_oem_props is not None:
680 script.AssertSomeThumbprint(
681 target_info.GetBuildProp("ro.build.thumbprint"),
682 source_info.GetBuildProp("ro.build.thumbprint"))
683 elif source_oem_props is None and target_oem_props is not None:
684 script.AssertFingerprintOrThumbprint(
685 source_info.fingerprint,
686 target_info.GetBuildProp("ro.build.thumbprint"))
687 else:
688 script.AssertFingerprintOrThumbprint(
689 target_info.fingerprint,
690 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700691
Doug Zongkerfc44a512014-08-26 13:10:25 -0700692
Tao Bao481bab82017-12-21 11:23:09 -0800693def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
694 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700695 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700696
697 Metadata used for on-device compatibility verification is retrieved from
698 target_zip then added to compatibility.zip which is added to the output_zip
699 archive.
700
Tao Baobcd1d162017-08-26 13:10:26 -0700701 Compatibility archive should only be included for devices that have enabled
702 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700703
704 Args:
705 target_zip: Zip file containing the source files to be included for OTA.
706 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800707 target_info: The BuildInfo instance that holds the target build info.
708 source_info: The BuildInfo instance that holds the source build info, if
709 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700710 """
711
Tao Baobcd1d162017-08-26 13:10:26 -0700712 def AddCompatibilityArchive(system_updated, vendor_updated):
713 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700714
Tao Baobcd1d162017-08-26 13:10:26 -0700715 Args:
716 system_updated: If True, the system image will be updated and therefore
717 its metadata should be included.
718 vendor_updated: If True, the vendor image will be updated and therefore
719 its metadata should be included.
720 """
721 # Determine what metadata we need. Files are names relative to META/.
722 compatibility_files = []
723 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
724 system_metadata = ("system_manifest.xml", "system_matrix.xml")
725 if vendor_updated:
726 compatibility_files += vendor_metadata
727 if system_updated:
728 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700729
Tao Baobcd1d162017-08-26 13:10:26 -0700730 # Create new archive.
731 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800732 compatibility_archive_zip = zipfile.ZipFile(
733 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700734
Tao Baobcd1d162017-08-26 13:10:26 -0700735 # Add metadata.
736 for file_name in compatibility_files:
737 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700738
Tao Baobcd1d162017-08-26 13:10:26 -0700739 if target_file_name in target_zip.namelist():
740 data = target_zip.read(target_file_name)
741 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700742
Tao Baobcd1d162017-08-26 13:10:26 -0700743 # Ensure files are written before we copy into output_zip.
744 compatibility_archive_zip.close()
745
746 # Only add the archive if we have any compatibility info.
747 if compatibility_archive_zip.namelist():
748 common.ZipWrite(output_zip, compatibility_archive.name,
749 arcname="compatibility.zip",
750 compress_type=zipfile.ZIP_STORED)
751
752 # Will only proceed if the target has enabled the Treble support (as well as
753 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800754 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700755 return
756
xunchangabfa2652019-02-19 16:27:10 -0800757 # Skip adding the compatibility package as a workaround for b/114240221. The
758 # compatibility will always fail on devices without qualified kernels.
759 if OPTIONS.skip_compatibility_check:
760 return
761
Tao Baobcd1d162017-08-26 13:10:26 -0700762 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800763 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700764 AddCompatibilityArchive(True, True)
765 return
766
Tao Bao481bab82017-12-21 11:23:09 -0800767 source_fp = source_info.fingerprint
768 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700769 system_updated = source_fp != target_fp
770
Tao Baoea6cbd02018-09-05 13:06:37 -0700771 source_fp_vendor = source_info.vendor_fingerprint
772 target_fp_vendor = target_info.vendor_fingerprint
773 # vendor build fingerprints could be possibly blacklisted at build time. For
774 # such a case, we consider the vendor images being changed.
775 if source_fp_vendor is None or target_fp_vendor is None:
776 vendor_updated = True
777 else:
778 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700779
780 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700781
782
Tao Bao491d7e22018-02-21 13:17:22 -0800783def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800784 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700785
Tao Bao481bab82017-12-21 11:23:09 -0800786 # We don't know what version it will be installed on top of. We expect the API
787 # just won't change very often. Similarly for fstab, it might have changed in
788 # the target build.
789 target_api_version = target_info["recovery_api_version"]
790 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700791
Tao Bao481bab82017-12-21 11:23:09 -0800792 if target_info.oem_props and not OPTIONS.oem_no_mount:
793 target_info.WriteMountOemScript(script)
794
Tao Baodf3a48b2018-01-10 16:30:43 -0800795 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700796
Tao Bao491d7e22018-02-21 13:17:22 -0800797 if not OPTIONS.no_signing:
798 staging_file = common.MakeTempFile(suffix='.zip')
799 else:
800 staging_file = output_file
801
802 output_zip = zipfile.ZipFile(
803 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
804
Doug Zongker05d3dea2009-06-22 11:32:31 -0700805 device_specific = common.DeviceSpecificParams(
806 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800807 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700808 output_zip=output_zip,
809 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700810 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700811 metadata=metadata,
812 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700813
Tao Bao457cbf62017-03-06 09:56:01 -0800814 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800815
Tao Bao481bab82017-12-21 11:23:09 -0800816 # Assertions (e.g. downgrade check, device properties check).
817 ts = target_info.GetBuildProp("ro.build.date.utc")
818 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700819 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700820
Tao Bao481bab82017-12-21 11:23:09 -0800821 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700822 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800823
824 # Two-step package strategy (in chronological order, which is *not*
825 # the order in which the generated script has things):
826 #
827 # if stage is not "2/3" or "3/3":
828 # write recovery image to boot partition
829 # set stage to "2/3"
830 # reboot to boot partition and restart recovery
831 # else if stage is "2/3":
832 # write recovery image to recovery partition
833 # set stage to "3/3"
834 # reboot to recovery partition and restart recovery
835 # else:
836 # (stage must be "3/3")
837 # set stage to ""
838 # do normal full package installation:
839 # wipe and install system, boot image, etc.
840 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700841 # complete script normally
842 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800843
844 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
845 OPTIONS.input_tmp, "RECOVERY")
846 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800847 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800848 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800849 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800850 assert fs.fs_type.upper() == "EMMC", \
851 "two-step packages only supported on devices with EMMC /misc partitions"
852 bcb_dev = {"bcb_dev": fs.device}
853 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
854 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700855if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800856""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800857
858 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
859 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800860 script.WriteRawImage("/recovery", "recovery.img")
861 script.AppendExtra("""
862set_stage("%(bcb_dev)s", "3/3");
863reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700864else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800865""" % bcb_dev)
866
Tao Baod42e97e2016-11-30 12:11:57 -0800867 # Stage 3/3: Make changes.
868 script.Comment("Stage 3/3")
869
Tao Bao6c55a8a2015-04-08 15:30:27 -0700870 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800871 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700872
Doug Zongkere5ff5902012-01-17 10:55:37 -0800873 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700874
Doug Zongker01ce19c2014-02-04 13:48:15 -0800875 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700876
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700877 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800878 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700879 if HasVendorPartition(input_zip):
880 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700881
Doug Zongker4b9596f2014-06-09 14:15:45 -0700882 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800883
Tao Baoe709b092018-02-07 12:40:00 -0800884 # See the notes in WriteBlockIncrementalOTAPackage().
885 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
886
Yifan Hong10c530d2018-12-27 17:34:18 -0800887 def GetBlockDifference(partition):
888 # Full OTA is done as an "incremental" against an empty source image. This
889 # has the effect of writing new data from the package to the entire
890 # partition, but lets us reuse the updater code that writes incrementals to
891 # do it.
892 tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
893 allow_shared_blocks)
894 tgt.ResetFileMap()
895 diff = common.BlockDifference(partition, tgt, src=None)
896 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700897
Yifan Hong10c530d2018-12-27 17:34:18 -0800898 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
899 if device_specific_diffs:
900 assert all(isinstance(diff, common.BlockDifference)
901 for diff in device_specific_diffs), \
902 "FullOTA_GetBlockDifferences is not returning a list of " \
903 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800904
Yifan Hong10c530d2018-12-27 17:34:18 -0800905 progress_dict = dict()
906 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700907 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800908 block_diffs.append(GetBlockDifference("vendor"))
909 progress_dict["vendor"] = 0.1
910 if device_specific_diffs:
911 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700912
Yifan Hong10c530d2018-12-27 17:34:18 -0800913 if target_info.get('use_dynamic_partitions') == "true":
914 # Use empty source_info_dict to indicate that all partitions / groups must
915 # be re-added.
916 dynamic_partitions_diff = common.DynamicPartitionsDifference(
917 info_dict=OPTIONS.info_dict,
918 block_diffs=block_diffs,
919 progress_dict=progress_dict)
920 dynamic_partitions_diff.WriteScript(script, output_zip,
921 write_verify_script=OPTIONS.verify)
922 else:
923 for block_diff in block_diffs:
924 block_diff.WriteScript(script, output_zip,
925 progress=progress_dict.get(block_diff.partition),
926 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700927
Tao Bao481bab82017-12-21 11:23:09 -0800928 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700929
Yifan Hong10c530d2018-12-27 17:34:18 -0800930 boot_img = common.GetBootableImage(
931 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800932 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700933 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700934
Doug Zongker01ce19c2014-02-04 13:48:15 -0800935 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700936 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700937
Doug Zongker01ce19c2014-02-04 13:48:15 -0800938 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700939 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700940
Doug Zongker1c390a22009-05-14 19:06:36 -0700941 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700942 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700943
Doug Zongker14833602010-02-02 13:12:04 -0800944 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800945
Doug Zongker922206e2014-03-04 13:16:24 -0800946 if OPTIONS.wipe_user_data:
947 script.ShowProgress(0.1, 10)
948 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700949
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800950 if OPTIONS.two_step:
951 script.AppendExtra("""
952set_stage("%(bcb_dev)s", "");
953""" % bcb_dev)
954 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800955
956 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
957 script.Comment("Stage 1/3")
958 _WriteRecoveryImageToBoot(script, output_zip)
959
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800960 script.AppendExtra("""
961set_stage("%(bcb_dev)s", "2/3");
962reboot_now("%(bcb_dev)s", "");
963endif;
964endif;
965""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800966
Tao Bao5d182562016-02-23 11:38:39 -0800967 script.SetProgress(1)
968 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800969 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800970
971 # We haven't written the metadata entry, which will be done in
972 # FinalizeMetadata.
973 common.ZipClose(output_zip)
974
975 needed_property_files = (
976 NonAbOtaPropertyFiles(),
977 )
978 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700979
Doug Zongkerfc44a512014-08-26 13:10:25 -0700980
Doug Zongker2ea21062010-04-28 16:05:21 -0700981def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800982 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
983 common.ZipWriteStr(output_zip, METADATA_NAME, value,
984 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700985
Doug Zongkerfc44a512014-08-26 13:10:25 -0700986
Tao Bao481bab82017-12-21 11:23:09 -0800987def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800988 # Only incremental OTAs are allowed to reach here.
989 assert OPTIONS.incremental_source is not None
990
Tao Bao481bab82017-12-21 11:23:09 -0800991 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
992 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800993 is_downgrade = long(post_timestamp) < long(pre_timestamp)
994
995 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800996 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700997 raise RuntimeError(
998 "--downgrade or --override_timestamp specified but no downgrade "
999 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -08001000 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -08001001 else:
1002 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -07001003 raise RuntimeError(
1004 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
1005 "Need to specify --override_timestamp OR --downgrade to allow "
1006 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -08001007
1008
Tao Baodf3a48b2018-01-10 16:30:43 -08001009def GetPackageMetadata(target_info, source_info=None):
1010 """Generates and returns the metadata dict.
1011
1012 It generates a dict() that contains the info to be written into an OTA
1013 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001014 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001015
1016 Args:
1017 target_info: The BuildInfo instance that holds the target build info.
1018 source_info: The BuildInfo instance that holds the source build info, or
1019 None if generating full OTA.
1020
1021 Returns:
1022 A dict to be written into package metadata entry.
1023 """
1024 assert isinstance(target_info, BuildInfo)
1025 assert source_info is None or isinstance(source_info, BuildInfo)
1026
1027 metadata = {
1028 'post-build' : target_info.fingerprint,
1029 'post-build-incremental' : target_info.GetBuildProp(
1030 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001031 'post-sdk-level' : target_info.GetBuildProp(
1032 'ro.build.version.sdk'),
1033 'post-security-patch-level' : target_info.GetBuildProp(
1034 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001035 }
1036
1037 if target_info.is_ab:
1038 metadata['ota-type'] = 'AB'
1039 metadata['ota-required-cache'] = '0'
1040 else:
1041 metadata['ota-type'] = 'BLOCK'
1042
1043 if OPTIONS.wipe_user_data:
1044 metadata['ota-wipe'] = 'yes'
1045
1046 is_incremental = source_info is not None
1047 if is_incremental:
1048 metadata['pre-build'] = source_info.fingerprint
1049 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1050 'ro.build.version.incremental')
1051 metadata['pre-device'] = source_info.device
1052 else:
1053 metadata['pre-device'] = target_info.device
1054
Tao Baofaa8e0b2018-04-12 14:31:43 -07001055 # Use the actual post-timestamp, even for a downgrade case.
1056 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1057
1058 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001059 if is_incremental:
1060 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001061
1062 return metadata
1063
1064
Tao Baod3fc38a2018-03-08 16:09:01 -08001065class PropertyFiles(object):
1066 """A class that computes the property-files string for an OTA package.
1067
1068 A property-files string is a comma-separated string that contains the
1069 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1070 can be fetched directly with the package URL along with the offset/size info.
1071 These strings can be used for streaming A/B OTAs, or allowing an updater to
1072 download package metadata entry directly, without paying the cost of
1073 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001074
Tao Baocc8e2662018-03-01 19:30:00 -08001075 Computing the final property-files string requires two passes. Because doing
1076 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1077 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1078 values.
1079
1080 This class provides functions to be called for each pass. The general flow is
1081 as follows.
1082
Tao Baod3fc38a2018-03-08 16:09:01 -08001083 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001084 # The first pass, which writes placeholders before doing initial signing.
1085 property_files.Compute()
1086 SignOutput()
1087
1088 # The second pass, by replacing the placeholders with actual data.
1089 property_files.Finalize()
1090 SignOutput()
1091
1092 And the caller can additionally verify the final result.
1093
1094 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001095 """
1096
Tao Baocc8e2662018-03-01 19:30:00 -08001097 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001098 self.name = None
1099 self.required = ()
1100 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001101
Tao Baocc8e2662018-03-01 19:30:00 -08001102 def Compute(self, input_zip):
1103 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001104
Tao Baocc8e2662018-03-01 19:30:00 -08001105 We reserve extra space for the offset and size of the metadata entry itself,
1106 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001107
Tao Baocc8e2662018-03-01 19:30:00 -08001108 Args:
1109 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001110
Tao Baocc8e2662018-03-01 19:30:00 -08001111 Returns:
1112 A string with placeholders for the metadata offset/size info, e.g.
1113 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1114 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001115 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001116
Tao Baod2ce2ed2018-03-16 12:59:42 -07001117 class InsufficientSpaceException(Exception):
1118 pass
1119
Tao Baocc8e2662018-03-01 19:30:00 -08001120 def Finalize(self, input_zip, reserved_length):
1121 """Finalizes a property-files string with actual METADATA offset/size info.
1122
1123 The input ZIP file has been signed, with the ZIP entries in the desired
1124 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1125 the ZIP entry offsets and construct the property-files string with actual
1126 data. Note that during this process, we must pad the property-files string
1127 to the reserved length, so that the METADATA entry size remains the same.
1128 Otherwise the entries' offsets and sizes may change again.
1129
1130 Args:
1131 input_zip: The input ZIP file.
1132 reserved_length: The reserved length of the property-files string during
1133 the call to Compute(). The final string must be no more than this
1134 size.
1135
1136 Returns:
1137 A property-files string including the metadata offset/size info, e.g.
1138 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1139
1140 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001141 InsufficientSpaceException: If the reserved length is insufficient to hold
1142 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001143 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001144 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001145 if len(result) > reserved_length:
1146 raise self.InsufficientSpaceException(
1147 'Insufficient reserved space: reserved={}, actual={}'.format(
1148 reserved_length, len(result)))
1149
Tao Baocc8e2662018-03-01 19:30:00 -08001150 result += ' ' * (reserved_length - len(result))
1151 return result
1152
1153 def Verify(self, input_zip, expected):
1154 """Verifies the input ZIP file contains the expected property-files string.
1155
1156 Args:
1157 input_zip: The input ZIP file.
1158 expected: The property-files string that's computed from Finalize().
1159
1160 Raises:
1161 AssertionError: On finding a mismatch.
1162 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001163 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001164 assert actual == expected, \
1165 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1166
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001167 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1168 """
1169 Constructs the property-files string per request.
1170
1171 Args:
1172 zip_file: The input ZIP file.
1173 reserved_length: The reserved length of the property-files string.
1174
1175 Returns:
1176 A property-files string including the metadata offset/size info, e.g.
1177 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1178 """
Tao Baocc8e2662018-03-01 19:30:00 -08001179
1180 def ComputeEntryOffsetSize(name):
1181 """Computes the zip entry offset and size."""
1182 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001183 offset = info.header_offset
1184 offset += zipfile.sizeFileHeader
1185 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001186 size = info.file_size
1187 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1188
1189 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001190 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001191 for entry in self.required:
1192 tokens.append(ComputeEntryOffsetSize(entry))
1193 for entry in self.optional:
1194 if entry in zip_file.namelist():
1195 tokens.append(ComputeEntryOffsetSize(entry))
1196
1197 # 'META-INF/com/android/metadata' is required. We don't know its actual
1198 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001199 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1200 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1201 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1202 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001203 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001204 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001205 else:
1206 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1207
1208 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001209
Tao Bao85f16982018-03-08 16:28:33 -08001210 def _GetPrecomputed(self, input_zip):
1211 """Computes the additional tokens to be included into the property-files.
1212
1213 This applies to tokens without actual ZIP entries, such as
1214 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1215 that they can download the payload metadata directly with the info.
1216
1217 Args:
1218 input_zip: The input zip file.
1219
1220 Returns:
1221 A list of strings (tokens) to be added to the property-files string.
1222 """
1223 # pylint: disable=no-self-use
1224 # pylint: disable=unused-argument
1225 return []
1226
Tao Baofe5b69a2018-03-02 09:47:43 -08001227
Tao Baod3fc38a2018-03-08 16:09:01 -08001228class StreamingPropertyFiles(PropertyFiles):
1229 """A subclass for computing the property-files for streaming A/B OTAs."""
1230
1231 def __init__(self):
1232 super(StreamingPropertyFiles, self).__init__()
1233 self.name = 'ota-streaming-property-files'
1234 self.required = (
1235 # payload.bin and payload_properties.txt must exist.
1236 'payload.bin',
1237 'payload_properties.txt',
1238 )
1239 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001240 # care_map is available only if dm-verity is enabled.
1241 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001242 'care_map.txt',
1243 # compatibility.zip is available only if target supports Treble.
1244 'compatibility.zip',
1245 )
1246
1247
Tao Bao85f16982018-03-08 16:28:33 -08001248class AbOtaPropertyFiles(StreamingPropertyFiles):
1249 """The property-files for A/B OTA that includes payload_metadata.bin info.
1250
1251 Since P, we expose one more token (aka property-file), in addition to the ones
1252 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1253 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1254 doesn't exist as a separate ZIP entry, but can be used to verify if the
1255 payload can be applied on the given device.
1256
1257 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1258 and the newly added 'ota-property-files' in P. The new token will only be
1259 available in 'ota-property-files'.
1260 """
1261
1262 def __init__(self):
1263 super(AbOtaPropertyFiles, self).__init__()
1264 self.name = 'ota-property-files'
1265
1266 def _GetPrecomputed(self, input_zip):
1267 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1268 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1269
1270 @staticmethod
1271 def _GetPayloadMetadataOffsetAndSize(input_zip):
1272 """Computes the offset and size of the payload metadata for a given package.
1273
1274 (From system/update_engine/update_metadata.proto)
1275 A delta update file contains all the deltas needed to update a system from
1276 one specific version to another specific version. The update format is
1277 represented by this struct pseudocode:
1278
1279 struct delta_update_file {
1280 char magic[4] = "CrAU";
1281 uint64 file_format_version;
1282 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1283
1284 // Only present if format_version > 1:
1285 uint32 metadata_signature_size;
1286
1287 // The Bzip2 compressed DeltaArchiveManifest
1288 char manifest[metadata_signature_size];
1289
1290 // The signature of the metadata (from the beginning of the payload up to
1291 // this location, not including the signature itself). This is a
1292 // serialized Signatures message.
1293 char medatada_signature_message[metadata_signature_size];
1294
1295 // Data blobs for files, no specific format. The specific offset
1296 // and length of each data blob is recorded in the DeltaArchiveManifest.
1297 struct {
1298 char data[];
1299 } blobs[];
1300
1301 // These two are not signed:
1302 uint64 payload_signatures_message_size;
1303 char payload_signatures_message[];
1304 };
1305
1306 'payload-metadata.bin' contains all the bytes from the beginning of the
1307 payload, till the end of 'medatada_signature_message'.
1308 """
1309 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001310 payload_offset = payload_info.header_offset
1311 payload_offset += zipfile.sizeFileHeader
1312 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001313 payload_size = payload_info.file_size
1314
1315 with input_zip.open('payload.bin', 'r') as payload_fp:
1316 header_bin = payload_fp.read(24)
1317
1318 # network byte order (big-endian)
1319 header = struct.unpack("!IQQL", header_bin)
1320
1321 # 'CrAU'
1322 magic = header[0]
1323 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1324
1325 manifest_size = header[2]
1326 metadata_signature_size = header[3]
1327 metadata_total = 24 + manifest_size + metadata_signature_size
1328 assert metadata_total < payload_size
1329
1330 return (payload_offset, metadata_total)
1331
1332
Tao Bao491d7e22018-02-21 13:17:22 -08001333class NonAbOtaPropertyFiles(PropertyFiles):
1334 """The property-files for non-A/B OTA.
1335
1336 For non-A/B OTA, the property-files string contains the info for METADATA
1337 entry, with which a system updater can be fetched the package metadata prior
1338 to downloading the entire package.
1339 """
1340
1341 def __init__(self):
1342 super(NonAbOtaPropertyFiles, self).__init__()
1343 self.name = 'ota-property-files'
1344
1345
Tao Baod3fc38a2018-03-08 16:09:01 -08001346def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001347 """Finalizes the metadata and signs an A/B OTA package.
1348
1349 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1350 that contains the offsets and sizes for the ZIP entries. An example
1351 property-files string is as follows.
1352
1353 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1354
1355 OTA server can pass down this string, in addition to the package URL, to the
1356 system update client. System update client can then fetch individual ZIP
1357 entries (ZIP_STORED) directly at the given offset of the URL.
1358
1359 Args:
1360 metadata: The metadata dict for the package.
1361 input_file: The input ZIP filename that doesn't contain the package METADATA
1362 entry yet.
1363 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001364 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001365 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001366
Tao Baod2ce2ed2018-03-16 12:59:42 -07001367 def ComputeAllPropertyFiles(input_file, needed_property_files):
1368 # Write the current metadata entry with placeholders.
1369 with zipfile.ZipFile(input_file) as input_zip:
1370 for property_files in needed_property_files:
1371 metadata[property_files.name] = property_files.Compute(input_zip)
1372 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001373
Tao Baod2ce2ed2018-03-16 12:59:42 -07001374 if METADATA_NAME in namelist:
1375 common.ZipDelete(input_file, METADATA_NAME)
1376 output_zip = zipfile.ZipFile(input_file, 'a')
1377 WriteMetadata(metadata, output_zip)
1378 common.ZipClose(output_zip)
1379
1380 if OPTIONS.no_signing:
1381 return input_file
1382
Tao Bao491d7e22018-02-21 13:17:22 -08001383 prelim_signing = common.MakeTempFile(suffix='.zip')
1384 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001385 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001386
Tao Baod2ce2ed2018-03-16 12:59:42 -07001387 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1388 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1389 for property_files in needed_property_files:
1390 metadata[property_files.name] = property_files.Finalize(
1391 prelim_signing_zip, len(metadata[property_files.name]))
1392
1393 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1394 # entries, as well as padding the entry headers. We do a preliminary signing
1395 # (with an incomplete metadata entry) to allow that to happen. Then compute
1396 # the ZIP entry offsets, write back the final metadata and do the final
1397 # signing.
1398 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1399 try:
1400 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1401 except PropertyFiles.InsufficientSpaceException:
1402 # Even with the preliminary signing, the entry orders may change
1403 # dramatically, which leads to insufficiently reserved space during the
1404 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1405 # preliminary signing works, based on the already ordered ZIP entries, to
1406 # address the issue.
1407 prelim_signing = ComputeAllPropertyFiles(
1408 prelim_signing, needed_property_files)
1409 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001410
1411 # Replace the METADATA entry.
1412 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001413 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001414 WriteMetadata(metadata, output_zip)
1415 common.ZipClose(output_zip)
1416
1417 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001418 if OPTIONS.no_signing:
1419 output_file = prelim_signing
1420 else:
1421 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001422
1423 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001424 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001425 for property_files in needed_property_files:
1426 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001427
1428
Tao Bao491d7e22018-02-21 13:17:22 -08001429def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001430 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1431 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001432
Tao Bao481bab82017-12-21 11:23:09 -08001433 target_api_version = target_info["recovery_api_version"]
1434 source_api_version = source_info["recovery_api_version"]
1435 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001436 logger.warning(
1437 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001438
Tao Bao481bab82017-12-21 11:23:09 -08001439 script = edify_generator.EdifyGenerator(
1440 source_api_version, target_info, fstab=source_info["fstab"])
1441
1442 if target_info.oem_props or source_info.oem_props:
1443 if not OPTIONS.oem_no_mount:
1444 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001445
Tao Baodf3a48b2018-01-10 16:30:43 -08001446 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001447
Tao Bao491d7e22018-02-21 13:17:22 -08001448 if not OPTIONS.no_signing:
1449 staging_file = common.MakeTempFile(suffix='.zip')
1450 else:
1451 staging_file = output_file
1452
1453 output_zip = zipfile.ZipFile(
1454 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1455
Geremy Condra36bd3652014-02-06 19:45:10 -08001456 device_specific = common.DeviceSpecificParams(
1457 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001458 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001459 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001460 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001461 output_zip=output_zip,
1462 script=script,
1463 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001464 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001465
Geremy Condra36bd3652014-02-06 19:45:10 -08001466 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001467 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001468 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001469 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001470 updating_boot = (not OPTIONS.two_step and
1471 (source_boot.data != target_boot.data))
1472
Geremy Condra36bd3652014-02-06 19:45:10 -08001473 target_recovery = common.GetBootableImage(
1474 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001475
Tao Baoe709b092018-02-07 12:40:00 -08001476 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1477 # shared blocks (i.e. some blocks will show up in multiple files' block
1478 # list). We can only allocate such shared blocks to the first "owner", and
1479 # disable imgdiff for all later occurrences.
1480 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1481 target_info.get('ext4_share_dup_blocks') == "true")
1482 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1483 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001484
1485 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1486 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001487 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001488 allow_shared_blocks,
1489 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001490
Tao Bao0582cb62017-12-21 11:47:01 -08001491 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001492 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001493 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001494
Tao Baof8acad12016-07-07 09:09:58 -07001495 # Check the first block of the source system partition for remount R/W only
1496 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001497 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001498 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001499 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1500 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1501 # b) the blocks listed in block map may not contain all the bytes for a given
1502 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001503 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001504 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1505 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001506 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001507 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001508 version=blockimgdiff_version,
1509 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001510
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001511 if HasVendorPartition(target_zip):
1512 if not HasVendorPartition(source_zip):
1513 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001514 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1515 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001516 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1517 "vendor", 4096, target_info)
1518 vendor_tgt = common.GetSparseImage(
1519 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1520 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001521
1522 # Check first block of vendor partition for remount R/W only if
1523 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001524 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001525 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001526 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001527 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001528 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001529 version=blockimgdiff_version,
1530 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001531 else:
1532 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001533
Tao Baobcd1d162017-08-26 13:10:26 -07001534 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001535 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001536
Tao Bao481bab82017-12-21 11:23:09 -08001537 # Assertions (e.g. device properties check).
1538 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001539 device_specific.IncrementalOTA_Assertions()
1540
1541 # Two-step incremental package strategy (in chronological order,
1542 # which is *not* the order in which the generated script has
1543 # things):
1544 #
1545 # if stage is not "2/3" or "3/3":
1546 # do verification on current system
1547 # write recovery image to boot partition
1548 # set stage to "2/3"
1549 # reboot to boot partition and restart recovery
1550 # else if stage is "2/3":
1551 # write recovery image to recovery partition
1552 # set stage to "3/3"
1553 # reboot to recovery partition and restart recovery
1554 # else:
1555 # (stage must be "3/3")
1556 # perform update:
1557 # patch system files, etc.
1558 # force full install of new boot image
1559 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001560 # complete script normally
1561 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001562
1563 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001564 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001565 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001566 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001567 assert fs.fs_type.upper() == "EMMC", \
1568 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001569 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001570 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1571 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001572if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001573""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001574
1575 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1576 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001577 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001578 script.WriteRawImage("/recovery", "recovery.img")
1579 script.AppendExtra("""
1580set_stage("%(bcb_dev)s", "3/3");
1581reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001582else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001583""" % bcb_dev)
1584
Tao Baod42e97e2016-11-30 12:11:57 -08001585 # Stage 1/3: (a) Verify the current system.
1586 script.Comment("Stage 1/3")
1587
Tao Bao6c55a8a2015-04-08 15:30:27 -07001588 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001589 script.Print("Source: {}".format(source_info.fingerprint))
1590 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001591
Geremy Condra36bd3652014-02-06 19:45:10 -08001592 script.Print("Verifying current system...")
1593
1594 device_specific.IncrementalOTA_VerifyBegin()
1595
Tao Bao481bab82017-12-21 11:23:09 -08001596 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001597
Tao Baod8d14be2016-02-04 14:26:02 -08001598 # Check the required cache size (i.e. stashed blocks).
1599 size = []
1600 if system_diff:
1601 size.append(system_diff.required_cache)
1602 if vendor_diff:
1603 size.append(vendor_diff.required_cache)
1604
Geremy Condra36bd3652014-02-06 19:45:10 -08001605 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001606 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001607 d = common.Difference(target_boot, source_boot)
1608 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001609 if d is None:
1610 include_full_boot = True
1611 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1612 else:
1613 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001614
Tao Bao32fcdab2018-10-12 10:30:39 -07001615 logger.info(
1616 "boot target: %d source: %d diff: %d", target_boot.size,
1617 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001618
Tao Bao51216552018-08-26 11:53:15 -07001619 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001620
Tao Bao51216552018-08-26 11:53:15 -07001621 script.PatchPartitionCheck(
1622 "{}:{}:{}:{}".format(
1623 boot_type, boot_device, target_boot.size, target_boot.sha1),
1624 "{}:{}:{}:{}".format(
1625 boot_type, boot_device, source_boot.size, source_boot.sha1))
1626
Tao Baod8d14be2016-02-04 14:26:02 -08001627 size.append(target_boot.size)
1628
1629 if size:
1630 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001631
1632 device_specific.IncrementalOTA_VerifyEnd()
1633
1634 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001635 # Stage 1/3: (b) Write recovery image to /boot.
1636 _WriteRecoveryImageToBoot(script, output_zip)
1637
Geremy Condra36bd3652014-02-06 19:45:10 -08001638 script.AppendExtra("""
1639set_stage("%(bcb_dev)s", "2/3");
1640reboot_now("%(bcb_dev)s", "");
1641else
1642""" % bcb_dev)
1643
Tao Baod42e97e2016-11-30 12:11:57 -08001644 # Stage 3/3: Make changes.
1645 script.Comment("Stage 3/3")
1646
Jesse Zhao75bcea02015-01-06 10:59:53 -08001647 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001648 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001649 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001650 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001651 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1652 if device_specific_diffs:
1653 assert all(isinstance(diff, common.BlockDifference)
1654 for diff in device_specific_diffs), \
1655 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1656 "BlockDifference objects"
1657 for diff in device_specific_diffs:
1658 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001659
Geremy Condra36bd3652014-02-06 19:45:10 -08001660 script.Comment("---- start making changes here ----")
1661
1662 device_specific.IncrementalOTA_InstallBegin()
1663
Yifan Hong10c530d2018-12-27 17:34:18 -08001664 block_diffs = [system_diff]
1665 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001666 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001667 block_diffs.append(vendor_diff)
1668 progress_dict["vendor"] = 0.1
1669 if device_specific_diffs:
1670 block_diffs += device_specific_diffs
1671
1672 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1673 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1674 raise RuntimeError(
1675 "can't generate incremental that disables dynamic partitions")
1676 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1677 info_dict=OPTIONS.target_info_dict,
1678 source_info_dict=OPTIONS.source_info_dict,
1679 block_diffs=block_diffs,
1680 progress_dict=progress_dict)
1681 dynamic_partitions_diff.WriteScript(
1682 script, output_zip, write_verify_script=OPTIONS.verify)
1683 else:
1684 for block_diff in block_diffs:
1685 block_diff.WriteScript(script, output_zip,
1686 progress=progress_dict.get(block_diff.partition),
1687 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001688
1689 if OPTIONS.two_step:
1690 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1691 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001692 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001693
1694 if not OPTIONS.two_step:
1695 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001696 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001697 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001698 script.Print("Installing boot image...")
1699 script.WriteRawImage("/boot", "boot.img")
1700 else:
1701 # Produce the boot image by applying a patch to the current
1702 # contents of the boot partition, and write it back to the
1703 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001704 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001705 script.Print("Patching boot image...")
1706 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001707 script.PatchPartition(
1708 '{}:{}:{}:{}'.format(
1709 boot_type, boot_device, target_boot.size, target_boot.sha1),
1710 '{}:{}:{}:{}'.format(
1711 boot_type, boot_device, source_boot.size, source_boot.sha1),
1712 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001713 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001714 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001715
1716 # Do device-specific installation (eg, write radio image).
1717 device_specific.IncrementalOTA_InstallEnd()
1718
1719 if OPTIONS.extra_script is not None:
1720 script.AppendExtra(OPTIONS.extra_script)
1721
Doug Zongker922206e2014-03-04 13:16:24 -08001722 if OPTIONS.wipe_user_data:
1723 script.Print("Erasing user data...")
1724 script.FormatPartition("/data")
1725
Geremy Condra36bd3652014-02-06 19:45:10 -08001726 if OPTIONS.two_step:
1727 script.AppendExtra("""
1728set_stage("%(bcb_dev)s", "");
1729endif;
1730endif;
1731""" % bcb_dev)
1732
1733 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001734 # For downgrade OTAs, we prefer to use the update-binary in the source
1735 # build that is actually newer than the one in the target build.
1736 if OPTIONS.downgrade:
1737 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1738 else:
1739 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001740 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001741
1742 # We haven't written the metadata entry yet, which will be handled in
1743 # FinalizeMetadata().
1744 common.ZipClose(output_zip)
1745
1746 # Sign the generated zip package unless no_signing is specified.
1747 needed_property_files = (
1748 NonAbOtaPropertyFiles(),
1749 )
1750 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001751
Doug Zongker32b527d2014-03-04 10:03:02 -08001752
Tao Bao15a146a2018-02-21 16:06:59 -08001753def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001754 """Returns a target-files.zip file for generating secondary payload.
1755
1756 Although the original target-files.zip already contains secondary slot
1757 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1758 ones without _other suffix. Note that we cannot instead modify the names in
1759 META/ab_partitions.txt, because there are no matching partitions on device.
1760
1761 For the partitions that don't have secondary images, the ones for primary
1762 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1763 bootloader images in the inactive slot.
1764
1765 Args:
1766 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001767 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001768
1769 Returns:
1770 The filename of the target-files.zip for generating secondary payload.
1771 """
1772 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1773 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1774
Tao Baodba59ee2018-01-09 13:21:02 -08001775 with zipfile.ZipFile(input_file, 'r') as input_zip:
1776 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001777 namelist = input_zip.namelist()
1778
1779 # Additionally unzip 'RADIO/*' if exists.
1780 unzip_pattern = UNZIP_PATTERN[:]
1781 if any([entry.startswith('RADIO/') for entry in namelist]):
1782 unzip_pattern.append('RADIO/*')
1783 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001784
1785 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001786 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1787 if info.filename == 'IMAGES/system_other.img':
1788 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1789
1790 # Primary images and friends need to be skipped explicitly.
1791 elif info.filename in ('IMAGES/system.img',
1792 'IMAGES/system.map'):
1793 pass
1794
Tao Bao15a146a2018-02-21 16:06:59 -08001795 # Skip copying the postinstall config if requested.
1796 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1797 pass
1798
Tao Bao12489802018-07-12 14:47:38 -07001799 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001800 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1801
Tao Baof7140c02018-01-30 17:09:24 -08001802 common.ZipClose(target_zip)
1803
1804 return target_file
1805
1806
Tao Bao15a146a2018-02-21 16:06:59 -08001807def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1808 """Returns a target-files.zip that's not containing postinstall_config.txt.
1809
1810 This allows brillo_update_payload script to skip writing all the postinstall
1811 hooks in the generated payload. The input target-files.zip file will be
1812 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1813 contain the postinstall_config.txt entry, the input file will be returned.
1814
1815 Args:
1816 input_file: The input target-files.zip filename.
1817
1818 Returns:
1819 The filename of target-files.zip that doesn't contain postinstall config.
1820 """
1821 # We should only make a copy if postinstall_config entry exists.
1822 with zipfile.ZipFile(input_file, 'r') as input_zip:
1823 if POSTINSTALL_CONFIG not in input_zip.namelist():
1824 return input_file
1825
1826 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1827 shutil.copyfile(input_file, target_file)
1828 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1829 return target_file
1830
1831
Yifan Hong50e79542018-11-08 17:44:12 -08001832def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
1833 super_block_devices):
1834 """Returns a target-files.zip for retrofitting dynamic partitions.
1835
1836 This allows brillo_update_payload to generate an OTA based on the exact
1837 bits on the block devices. Postinstall is disabled.
1838
1839 Args:
1840 input_file: The input target-files.zip filename.
1841 super_block_devices: The list of super block devices
1842
1843 Returns:
1844 The filename of target-files.zip with *.img replaced with super_*.img for
1845 each block device in super_block_devices.
1846 """
1847 assert super_block_devices, "No super_block_devices are specified."
1848
1849 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001850 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001851
1852 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1853 shutil.copyfile(input_file, target_file)
1854
1855 with zipfile.ZipFile(input_file, 'r') as input_zip:
1856 namelist = input_zip.namelist()
1857
1858 # Always skip postinstall for a retrofit update.
1859 to_delete = [POSTINSTALL_CONFIG]
1860
1861 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1862 # is a regular update on devices without dynamic partitions support.
1863 to_delete += [DYNAMIC_PARTITION_INFO]
1864
Tao Bao03fecb62018-11-28 10:59:23 -08001865 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001866 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001867 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001868
1869 common.ZipDelete(target_file, to_delete)
1870
1871 input_tmp = common.UnzipTemp(input_file, SUPER_SPLIT_PATTERN)
1872 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1873
1874 # Write super_{foo}.img as {foo}.img.
1875 for src, dst in replace.items():
1876 assert src in namelist, \
1877 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1878 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1879 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1880
1881 common.ZipClose(target_zip)
1882
1883 return target_file
1884
1885
Tao Baoc098e9e2016-01-07 13:03:56 -08001886def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1887 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001888 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001889 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001890 if not OPTIONS.no_signing:
1891 staging_file = common.MakeTempFile(suffix='.zip')
1892 else:
1893 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001894 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001895 compression=zipfile.ZIP_DEFLATED)
1896
Tao Bao481bab82017-12-21 11:23:09 -08001897 if source_file is not None:
1898 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1899 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1900 else:
1901 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1902 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001903
Tao Bao481bab82017-12-21 11:23:09 -08001904 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001905 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001906
Yifan Hong50e79542018-11-08 17:44:12 -08001907 if OPTIONS.retrofit_dynamic_partitions:
1908 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
1909 target_file, target_info.get("super_block_devices").strip().split())
1910 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001911 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1912
Tao Bao40b18822018-01-30 18:19:04 -08001913 # Generate payload.
1914 payload = Payload()
1915
1916 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001917 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001918 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001919 else:
1920 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001921 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001922
Tao Bao40b18822018-01-30 18:19:04 -08001923 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001924
Tao Bao40b18822018-01-30 18:19:04 -08001925 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001926 payload_signer = PayloadSigner()
1927 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001928
Tao Bao40b18822018-01-30 18:19:04 -08001929 # Write the payload into output zip.
1930 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001931
Tao Baof7140c02018-01-30 17:09:24 -08001932 # Generate and include the secondary payload that installs secondary images
1933 # (e.g. system_other.img).
1934 if OPTIONS.include_secondary:
1935 # We always include a full payload for the secondary slot, even when
1936 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001937 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1938 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001939 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001940 secondary_payload.Generate(secondary_target_file,
1941 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001942 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001943 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001944
Tianjie Xucfa86222016-03-07 16:31:19 -08001945 # If dm-verity is supported for the device, copy contents of care_map
1946 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001947 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001948 if (target_info.get("verity") == "true" or
1949 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001950 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1951 "META/" + x in target_zip.namelist()]
1952
1953 # Adds care_map if either the protobuf format or the plain text one exists.
1954 if care_map_list:
1955 care_map_name = care_map_list[0]
1956 care_map_data = target_zip.read("META/" + care_map_name)
1957 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001958 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001959 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001960 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001961 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001962 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001963
Tao Baobcd1d162017-08-26 13:10:26 -07001964 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001965 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001966
Tao Bao21803d32017-04-19 10:16:09 -07001967 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001968
Tao Baofe5b69a2018-03-02 09:47:43 -08001969 # We haven't written the metadata entry yet, which will be handled in
1970 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001971 common.ZipClose(output_zip)
1972
Tao Bao85f16982018-03-08 16:28:33 -08001973 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1974 # all the info of the latter. However, system updaters and OTA servers need to
1975 # take time to switch to the new flag. We keep both of the flags for
1976 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001977 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001978 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001979 StreamingPropertyFiles(),
1980 )
1981 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001982
Tao Baoc098e9e2016-01-07 13:03:56 -08001983
Doug Zongkereef39442009-04-02 12:14:19 -07001984def main(argv):
1985
1986 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001987 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001988 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001989 elif o in ("-i", "--incremental_from"):
1990 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001991 elif o == "--full_radio":
1992 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001993 elif o == "--full_bootloader":
1994 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001995 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001996 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001997 elif o == "--downgrade":
1998 OPTIONS.downgrade = True
1999 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002000 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002001 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002002 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002003 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002004 elif o == "--oem_no_mount":
2005 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002006 elif o in ("-e", "--extra_script"):
2007 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002008 elif o in ("-t", "--worker_threads"):
2009 if a.isdigit():
2010 OPTIONS.worker_threads = int(a)
2011 else:
2012 raise ValueError("Cannot parse value %r for option %r - only "
2013 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002014 elif o in ("-2", "--two_step"):
2015 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002016 elif o == "--include_secondary":
2017 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002018 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002019 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002020 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002021 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002022 elif o == "--block":
2023 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002024 elif o in ("-b", "--binary"):
2025 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002026 elif o == "--stash_threshold":
2027 try:
2028 OPTIONS.stash_threshold = float(a)
2029 except ValueError:
2030 raise ValueError("Cannot parse value %r for option %r - expecting "
2031 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002032 elif o == "--log_diff":
2033 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002034 elif o == "--payload_signer":
2035 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002036 elif o == "--payload_signer_args":
2037 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002038 elif o == "--extracted_input_target_files":
2039 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002040 elif o == "--skip_postinstall":
2041 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002042 elif o == "--retrofit_dynamic_partitions":
2043 OPTIONS.retrofit_dynamic_partitions = True
xunchangabfa2652019-02-19 16:27:10 -08002044 elif o == "--skip_compatibility_check":
2045 OPTIONS.skip_compatibility_check = True
Doug Zongkereef39442009-04-02 12:14:19 -07002046 else:
2047 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002048 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002049
2050 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002051 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002052 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002053 "package_key=",
2054 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002055 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002056 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002057 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002058 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002059 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002060 "extra_script=",
2061 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002062 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002063 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002064 "no_signing",
2065 "block",
2066 "binary=",
2067 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002068 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002069 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002070 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002071 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002072 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002073 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002074 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002075 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002076 "retrofit_dynamic_partitions",
xunchangabfa2652019-02-19 16:27:10 -08002077 "skip_compatibility_check",
Dan Albert8b72aef2015-03-23 19:13:21 -07002078 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002079
2080 if len(args) != 2:
2081 common.Usage(__doc__)
2082 sys.exit(1)
2083
Tao Bao32fcdab2018-10-12 10:30:39 -07002084 common.InitLogging()
2085
Tao Bao5d182562016-02-23 11:38:39 -08002086 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002087 # We should only allow downgrading incrementals (as opposed to full).
2088 # Otherwise the device may go back from arbitrary build with this full
2089 # OTA package.
2090 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002091 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002092
Tao Bao2db13852018-01-08 22:28:57 -08002093 # Load the build info dicts from the zip directly or the extracted input
2094 # directory. We don't need to unzip the entire target-files zips, because they
2095 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2096 # When loading the info dicts, we don't need to provide the second parameter
2097 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2098 # some properties with their actual paths, such as 'selinux_fc',
2099 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002100 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002101 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002102 else:
Tao Bao2db13852018-01-08 22:28:57 -08002103 with zipfile.ZipFile(args[0], 'r') as input_zip:
2104 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002105
Tao Bao32fcdab2018-10-12 10:30:39 -07002106 logger.info("--- target info ---")
2107 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002108
2109 # Load the source build dict if applicable.
2110 if OPTIONS.incremental_source is not None:
2111 OPTIONS.target_info_dict = OPTIONS.info_dict
2112 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2113 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2114
Tao Bao32fcdab2018-10-12 10:30:39 -07002115 logger.info("--- source info ---")
2116 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002117
2118 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002119 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2120
Yifan Hong50e79542018-11-08 17:44:12 -08002121 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002122 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002123 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002124 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2125 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002126 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2127 raise common.ExternalError(
2128 "Expect to generate incremental OTA for retrofitting dynamic "
2129 "partitions, but dynamic_partition_retrofit is not set in target "
2130 "build.")
2131 logger.info("Implicitly generating retrofit incremental OTA.")
2132 OPTIONS.retrofit_dynamic_partitions = True
2133
2134 # Skip postinstall for retrofitting dynamic partitions.
2135 if OPTIONS.retrofit_dynamic_partitions:
2136 OPTIONS.skip_postinstall = True
2137
Tao Baoc098e9e2016-01-07 13:03:56 -08002138 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2139
Christian Oderf63e2cd2017-05-01 22:30:15 +02002140 # Use the default key to sign the package if not specified with package_key.
2141 # package_keys are needed on ab_updates, so always define them if an
2142 # ab_update is getting created.
2143 if not OPTIONS.no_signing or ab_update:
2144 if OPTIONS.package_key is None:
2145 OPTIONS.package_key = OPTIONS.info_dict.get(
2146 "default_system_dev_certificate",
2147 "build/target/product/security/testkey")
2148 # Get signing keys
2149 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2150
Tao Baoc098e9e2016-01-07 13:03:56 -08002151 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002152 WriteABOTAPackageWithBrilloScript(
2153 target_file=args[0],
2154 output_file=args[1],
2155 source_file=OPTIONS.incremental_source)
2156
Tao Bao32fcdab2018-10-12 10:30:39 -07002157 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002158 return
2159
Tao Bao2db13852018-01-08 22:28:57 -08002160 # Sanity check the loaded info dicts first.
2161 if OPTIONS.info_dict.get("no_recovery") == "true":
2162 raise common.ExternalError(
2163 "--- target build has specified no recovery ---")
2164
2165 # Non-A/B OTAs rely on /cache partition to store temporary files.
2166 cache_size = OPTIONS.info_dict.get("cache_size")
2167 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002168 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002169 OPTIONS.cache_size = cache_size
2170
Doug Zongker1c390a22009-05-14 19:06:36 -07002171 if OPTIONS.extra_script is not None:
2172 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2173
Dan Willemsencea5cd22017-03-21 14:44:27 -07002174 if OPTIONS.extracted_input is not None:
2175 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002176 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002177 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002178 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002179 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002180
Tao Bao2db13852018-01-08 22:28:57 -08002181 # If the caller explicitly specified the device-specific extensions path via
2182 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2183 # is present in the target target_files. Otherwise, take the path of the file
2184 # from 'tool_extensions' in the info dict and look for that in the local
2185 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002186 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002187 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2188 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002189 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002190 OPTIONS.device_specific = from_input
2191 else:
Tao Bao2db13852018-01-08 22:28:57 -08002192 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002193
Doug Zongker37974732010-09-16 17:44:38 -07002194 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002195 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002196
Tao Bao767e3ac2015-11-10 12:19:19 -08002197 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002198 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002199 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002200 WriteFullOTAPackage(
2201 input_zip,
2202 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002203
Tao Bao32b80dc2018-01-08 22:50:47 -08002204 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002205 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002206 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002207 OPTIONS.source_tmp = common.UnzipTemp(
2208 OPTIONS.incremental_source, UNZIP_PATTERN)
2209 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2210 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002211 WriteBlockIncrementalOTAPackage(
2212 input_zip,
2213 source_zip,
2214 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002215
2216 if OPTIONS.log_diff:
2217 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002218 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002219 target_files_diff.recursiveDiff(
2220 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002221
Tao Bao32fcdab2018-10-12 10:30:39 -07002222 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002223
2224
2225if __name__ == '__main__':
2226 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002227 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002228 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002229 except common.ExternalError:
2230 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002231 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002232 finally:
2233 common.Cleanup()