blob: 583af2d7712677fde1c25250b6f307ee89e67717 [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
67Non-A/B OTA specific options
68
69 -b (--binary) <file>
70 Use the given binary as the update-binary in the output package, instead
71 of the binary in the build's target_files. Use for development only.
72
73 --block
74 Generate a block-based OTA for non-A/B device. We have deprecated the
75 support for file-based OTA since O. Block-based OTA will be used by
76 default for all non-A/B devices. Keeping this flag here to not break
77 existing callers.
78
79 -e (--extra_script) <file>
80 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070081
leozwangaa6c1a12015-08-14 10:57:58 -070082 --full_bootloader
83 Similar to --full_radio. When generating an incremental OTA, always
84 include a full copy of bootloader image.
85
Tao Bao30df8b42018-04-23 15:32:53 -070086 --full_radio
87 When generating an incremental OTA, always include a full copy of radio
88 image. This option is only meaningful when -i is specified, because a full
89 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -070090
Tao Bao30df8b42018-04-23 15:32:53 -070091 --log_diff <file>
92 Generate a log file that shows the differences in the source and target
93 builds for an incremental package. This option is only meaningful when -i
94 is specified.
95
96 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080097 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -080098 properties on the OEM partition of the intended device. Multiple expected
99 values can be used by providing multiple files. Only the first dict will
100 be used to compute fingerprint, while the rest will be used to assert
101 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800102
Tao Bao8608cde2016-02-25 19:49:55 -0800103 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700104 For devices with OEM-specific properties but without an OEM partition, do
105 not mount the OEM partition in the updater-script. This should be very
106 rarely used, since it's expected to have a dedicated OEM partition for
107 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800108
Tao Bao30df8b42018-04-23 15:32:53 -0700109 --stash_threshold <float>
110 Specify the threshold that will be used to compute the maximum allowed
111 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700112
Tao Bao30df8b42018-04-23 15:32:53 -0700113 -t (--worker_threads) <int>
114 Specify the number of worker-threads that will be used when generating
115 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800116
Tao Bao30df8b42018-04-23 15:32:53 -0700117 --verify
118 Verify the checksums of the updated system and vendor (if any) partitions.
119 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700120
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800121 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700122 Generate a 'two-step' OTA package, where recovery is updated first, so
123 that any changes made to the system partition are done using the new
124 recovery (new kernel, etc.).
125
126A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800127
Tao Baof7140c02018-01-30 17:09:24 -0800128 --include_secondary
129 Additionally include the payload for secondary slot images (default:
130 False). Only meaningful when generating A/B OTAs.
131
132 By default, an A/B OTA package doesn't contain the images for the
133 secondary slot (e.g. system_other.img). Specifying this flag allows
134 generating a separate payload that will install secondary slot images.
135
136 Such a package needs to be applied in a two-stage manner, with a reboot
137 in-between. During the first stage, the updater applies the primary
138 payload only. Upon finishing, it reboots the device into the newly updated
139 slot. It then continues to install the secondary payload to the inactive
140 slot, but without switching the active slot at the end (needs the matching
141 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
142
143 Due to the special install procedure, the secondary payload will be always
144 generated as a full payload.
145
Tao Baodea0f8b2016-06-20 17:55:06 -0700146 --payload_signer <signer>
147 Specify the signer when signing the payload and metadata for A/B OTAs.
148 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
149 with the package private key. If the private key cannot be accessed
150 directly, a payload signer that knows how to do that should be specified.
151 The signer will be supplied with "-inkey <path_to_key>",
152 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700153
154 --payload_signer_args <args>
155 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800156
157 --skip_postinstall
158 Skip the postinstall hooks when generating an A/B OTA package (default:
159 False). Note that this discards ALL the hooks, including non-optional
160 ones. Should only be used if caller knows it's safe to do so (e.g. all the
161 postinstall work is to dexopt apps and a data wipe will happen immediately
162 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700163"""
164
Tao Bao89fbb0f2017-01-10 10:47:58 -0800165from __future__ import print_function
166
Doug Zongkerfc44a512014-08-26 13:10:25 -0700167import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800168import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700169import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800170import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800171import struct
Tao Bao481bab82017-12-21 11:23:09 -0800172import subprocess
173import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700174import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700175import zipfile
176
177import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700178import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -0700179
Tao Bao481bab82017-12-21 11:23:09 -0800180if sys.hexversion < 0x02070000:
181 print("Python 2.7 or newer is required.", file=sys.stderr)
182 sys.exit(1)
183
184
Doug Zongkereef39442009-04-02 12:14:19 -0700185OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700186OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700187OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700188OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700189OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700190OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800191OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700192OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700193OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
194if OPTIONS.worker_threads == 0:
195 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800196OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800197OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900198OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800199OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800200OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700201OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800202OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700203OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700204OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700205# Stash size cannot exceed cache_size * threshold.
206OPTIONS.cache_size = None
207OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800208OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700209OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700210OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700211OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200212OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800213OPTIONS.skip_postinstall = False
214
Tao Bao8dcf7382015-05-21 14:09:49 -0700215
Tao Bao2dd1c482017-02-03 16:49:39 -0800216METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800217POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800218UNZIP_PATTERN = ['IMAGES/*', 'META/*']
219
Tao Bao2dd1c482017-02-03 16:49:39 -0800220
Tao Bao481bab82017-12-21 11:23:09 -0800221class BuildInfo(object):
222 """A class that holds the information for a given build.
223
224 This class wraps up the property querying for a given source or target build.
225 It abstracts away the logic of handling OEM-specific properties, and caches
226 the commonly used properties such as fingerprint.
227
228 There are two types of info dicts: a) build-time info dict, which is generated
229 at build time (i.e. included in a target_files zip); b) OEM info dict that is
230 specified at package generation time (via command line argument
231 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
232 having "oem_fingerprint_properties" in build-time info dict), all the queries
233 would be answered based on build-time info dict only. Otherwise if using
234 OEM-specific properties, some of them will be calculated from two info dicts.
235
236 Users can query properties similarly as using a dict() (e.g. info['fstab']),
237 or to query build properties via GetBuildProp() or GetVendorBuildProp().
238
239 Attributes:
240 info_dict: The build-time info dict.
241 is_ab: Whether it's a build that uses A/B OTA.
242 oem_dicts: A list of OEM dicts.
243 oem_props: A list of OEM properties that should be read from OEM dicts; None
244 if the build doesn't use any OEM-specific property.
245 fingerprint: The fingerprint of the build, which would be calculated based
246 on OEM properties if applicable.
247 device: The device name, which could come from OEM dicts if applicable.
248 """
249
250 def __init__(self, info_dict, oem_dicts):
251 """Initializes a BuildInfo instance with the given dicts.
252
Tao Bao667c7532018-07-06 10:13:59 -0700253 Note that it only wraps up the given dicts, without making copies.
254
Tao Bao481bab82017-12-21 11:23:09 -0800255 Arguments:
256 info_dict: The build-time info dict.
257 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
258 that it always uses the first dict to calculate the fingerprint or the
259 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700260 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800261 """
262 self.info_dict = info_dict
263 self.oem_dicts = oem_dicts
264
265 self._is_ab = info_dict.get("ab_update") == "true"
266 self._oem_props = info_dict.get("oem_fingerprint_properties")
267
268 if self._oem_props:
269 assert oem_dicts, "OEM source required for this build"
270
271 # These two should be computed only after setting self._oem_props.
272 self._device = self.GetOemProperty("ro.product.device")
273 self._fingerprint = self.CalculateFingerprint()
274
275 @property
276 def is_ab(self):
277 return self._is_ab
278
279 @property
280 def device(self):
281 return self._device
282
283 @property
284 def fingerprint(self):
285 return self._fingerprint
286
287 @property
288 def oem_props(self):
289 return self._oem_props
290
291 def __getitem__(self, key):
292 return self.info_dict[key]
293
Tao Bao667c7532018-07-06 10:13:59 -0700294 def __setitem__(self, key, value):
295 self.info_dict[key] = value
296
Tao Bao481bab82017-12-21 11:23:09 -0800297 def get(self, key, default=None):
298 return self.info_dict.get(key, default)
299
Tao Bao667c7532018-07-06 10:13:59 -0700300 def items(self):
301 return self.info_dict.items()
302
Tao Bao481bab82017-12-21 11:23:09 -0800303 def GetBuildProp(self, prop):
304 """Returns the inquired build property."""
305 try:
306 return self.info_dict.get("build.prop", {})[prop]
307 except KeyError:
308 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
309
310 def GetVendorBuildProp(self, prop):
311 """Returns the inquired vendor build property."""
312 try:
313 return self.info_dict.get("vendor.build.prop", {})[prop]
314 except KeyError:
315 raise common.ExternalError(
316 "couldn't find %s in vendor.build.prop" % (prop,))
317
318 def GetOemProperty(self, key):
319 if self.oem_props is not None and key in self.oem_props:
320 return self.oem_dicts[0][key]
321 return self.GetBuildProp(key)
322
323 def CalculateFingerprint(self):
324 if self.oem_props is None:
325 return self.GetBuildProp("ro.build.fingerprint")
326 return "%s/%s/%s:%s" % (
327 self.GetOemProperty("ro.product.brand"),
328 self.GetOemProperty("ro.product.name"),
329 self.GetOemProperty("ro.product.device"),
330 self.GetBuildProp("ro.build.thumbprint"))
331
332 def WriteMountOemScript(self, script):
333 assert self.oem_props is not None
334 recovery_mount_options = self.info_dict.get("recovery_mount_options")
335 script.Mount("/oem", recovery_mount_options)
336
337 def WriteDeviceAssertions(self, script, oem_no_mount):
338 # Read the property directly if not using OEM properties.
339 if not self.oem_props:
340 script.AssertDevice(self.device)
341 return
342
343 # Otherwise assert OEM properties.
344 if not self.oem_dicts:
345 raise common.ExternalError(
346 "No OEM file provided to answer expected assertions")
347
348 for prop in self.oem_props.split():
349 values = []
350 for oem_dict in self.oem_dicts:
351 if prop in oem_dict:
352 values.append(oem_dict[prop])
353 if not values:
354 raise common.ExternalError(
355 "The OEM file is missing the property %s" % (prop,))
356 script.AssertOemProperty(prop, values, oem_no_mount)
357
358
Tao Baofabe0832018-01-17 15:52:28 -0800359class PayloadSigner(object):
360 """A class that wraps the payload signing works.
361
362 When generating a Payload, hashes of the payload and metadata files will be
363 signed with the device key, either by calling an external payload signer or
364 by calling openssl with the package key. This class provides a unified
365 interface, so that callers can just call PayloadSigner.Sign().
366
367 If an external payload signer has been specified (OPTIONS.payload_signer), it
368 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
369 that the signing key should be provided as part of the payload_signer_args.
370 Otherwise without an external signer, it uses the package key
371 (OPTIONS.package_key) and calls openssl for the signing works.
372 """
373
374 def __init__(self):
375 if OPTIONS.payload_signer is None:
376 # Prepare the payload signing key.
377 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
378 pw = OPTIONS.key_passwords[OPTIONS.package_key]
379
380 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
381 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
382 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
383 cmd.extend(["-out", signing_key])
384
385 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
386 stderr=subprocess.STDOUT)
387 stdoutdata, _ = get_signing_key.communicate()
388 assert get_signing_key.returncode == 0, \
389 "Failed to get signing key: {}".format(stdoutdata)
390
391 self.signer = "openssl"
392 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
393 "-pkeyopt", "digest:sha256"]
394 else:
395 self.signer = OPTIONS.payload_signer
396 self.signer_args = OPTIONS.payload_signer_args
397
398 def Sign(self, in_file):
399 """Signs the given input file. Returns the output filename."""
400 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
401 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
402 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
403 stdoutdata, _ = signing.communicate()
404 assert signing.returncode == 0, \
405 "Failed to sign the input file: {}".format(stdoutdata)
406 return out_file
407
408
Tao Bao40b18822018-01-30 18:19:04 -0800409class Payload(object):
410 """Manages the creation and the signing of an A/B OTA Payload."""
411
412 PAYLOAD_BIN = 'payload.bin'
413 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800414 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
415 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800416
Tao Bao667ff572018-02-10 00:02:40 -0800417 def __init__(self, secondary=False):
418 """Initializes a Payload instance.
419
420 Args:
421 secondary: Whether it's generating a secondary payload (default: False).
422 """
Tao Bao40b18822018-01-30 18:19:04 -0800423 # The place where the output from the subprocess should go.
424 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
425 self.payload_file = None
426 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800427 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800428
429 def Generate(self, target_file, source_file=None, additional_args=None):
430 """Generates a payload from the given target-files zip(s).
431
432 Args:
433 target_file: The filename of the target build target-files zip.
434 source_file: The filename of the source build target-files zip; or None if
435 generating a full OTA.
436 additional_args: A list of additional args that should be passed to
437 brillo_update_payload script; or None.
438 """
439 if additional_args is None:
440 additional_args = []
441
442 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
443 cmd = ["brillo_update_payload", "generate",
444 "--payload", payload_file,
445 "--target_image", target_file]
446 if source_file is not None:
447 cmd.extend(["--source_image", source_file])
448 cmd.extend(additional_args)
449 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
450 stdoutdata, _ = p.communicate()
451 assert p.returncode == 0, \
452 "brillo_update_payload generate failed: {}".format(stdoutdata)
453
454 self.payload_file = payload_file
455 self.payload_properties = None
456
457 def Sign(self, payload_signer):
458 """Generates and signs the hashes of the payload and metadata.
459
460 Args:
461 payload_signer: A PayloadSigner() instance that serves the signing work.
462
463 Raises:
464 AssertionError: On any failure when calling brillo_update_payload script.
465 """
466 assert isinstance(payload_signer, PayloadSigner)
467
468 # 1. Generate hashes of the payload and metadata files.
469 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
470 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
471 cmd = ["brillo_update_payload", "hash",
472 "--unsigned_payload", self.payload_file,
473 "--signature_size", "256",
474 "--metadata_hash_file", metadata_sig_file,
475 "--payload_hash_file", payload_sig_file]
476 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
477 p1.communicate()
478 assert p1.returncode == 0, "brillo_update_payload hash failed"
479
480 # 2. Sign the hashes.
481 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
482 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
483
484 # 3. Insert the signatures back into the payload file.
485 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
486 suffix=".bin")
487 cmd = ["brillo_update_payload", "sign",
488 "--unsigned_payload", self.payload_file,
489 "--payload", signed_payload_file,
490 "--signature_size", "256",
491 "--metadata_signature_file", signed_metadata_sig_file,
492 "--payload_signature_file", signed_payload_sig_file]
493 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
494 p1.communicate()
495 assert p1.returncode == 0, "brillo_update_payload sign failed"
496
497 # 4. Dump the signed payload properties.
498 properties_file = common.MakeTempFile(prefix="payload-properties-",
499 suffix=".txt")
500 cmd = ["brillo_update_payload", "properties",
501 "--payload", signed_payload_file,
502 "--properties_file", properties_file]
503 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
504 p1.communicate()
505 assert p1.returncode == 0, "brillo_update_payload properties failed"
506
Tao Bao667ff572018-02-10 00:02:40 -0800507 if self.secondary:
508 with open(properties_file, "a") as f:
509 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
510
Tao Bao40b18822018-01-30 18:19:04 -0800511 if OPTIONS.wipe_user_data:
512 with open(properties_file, "a") as f:
513 f.write("POWERWASH=1\n")
514
515 self.payload_file = signed_payload_file
516 self.payload_properties = properties_file
517
Tao Bao667ff572018-02-10 00:02:40 -0800518 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800519 """Writes the payload to the given zip.
520
521 Args:
522 output_zip: The output ZipFile instance.
523 """
524 assert self.payload_file is not None
525 assert self.payload_properties is not None
526
Tao Bao667ff572018-02-10 00:02:40 -0800527 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800528 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
529 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
530 else:
531 payload_arcname = Payload.PAYLOAD_BIN
532 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
533
Tao Bao40b18822018-01-30 18:19:04 -0800534 # Add the signed payload file and properties into the zip. In order to
535 # support streaming, we pack them as ZIP_STORED. So these entries can be
536 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800537 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800538 compress_type=zipfile.ZIP_STORED)
539 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800540 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800541 compress_type=zipfile.ZIP_STORED)
542
543
Doug Zongkereef39442009-04-02 12:14:19 -0700544def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200545 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700546
Doug Zongker951495f2009-08-14 12:44:19 -0700547 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
548 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700549
550
Tao Bao481bab82017-12-21 11:23:09 -0800551def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800552 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800553 if not oem_source:
554 return None
555
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800556 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800557 for oem_file in oem_source:
558 with open(oem_file) as fp:
559 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800560 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700561
Doug Zongkereef39442009-04-02 12:14:19 -0700562
Tao Baod42e97e2016-11-30 12:11:57 -0800563def _WriteRecoveryImageToBoot(script, output_zip):
564 """Find and write recovery image to /boot in two-step OTA.
565
566 In two-step OTAs, we write recovery image to /boot as the first step so that
567 we can reboot to there and install a new recovery image to /recovery.
568 A special "recovery-two-step.img" will be preferred, which encodes the correct
569 path of "/boot". Otherwise the device may show "device is corrupt" message
570 when booting into /boot.
571
572 Fall back to using the regular recovery.img if the two-step recovery image
573 doesn't exist. Note that rebuilding the special image at this point may be
574 infeasible, because we don't have the desired boot signer and keys when
575 calling ota_from_target_files.py.
576 """
577
578 recovery_two_step_img_name = "recovery-two-step.img"
579 recovery_two_step_img_path = os.path.join(
580 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
581 if os.path.exists(recovery_two_step_img_path):
582 recovery_two_step_img = common.GetBootableImage(
583 recovery_two_step_img_name, recovery_two_step_img_name,
584 OPTIONS.input_tmp, "RECOVERY")
585 common.ZipWriteStr(
586 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800587 print("two-step package: using %s in stage 1/3" % (
588 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800589 script.WriteRawImage("/boot", recovery_two_step_img_name)
590 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800591 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800592 # The "recovery.img" entry has been written into package earlier.
593 script.WriteRawImage("/boot", "recovery.img")
594
595
Doug Zongkerc9253822014-02-04 12:17:58 -0800596def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700597 namelist = [name for name in target_files_zip.namelist()]
598 return ("SYSTEM/recovery-from-boot.p" in namelist or
599 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700600
Tao Bao457cbf62017-03-06 09:56:01 -0800601
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700602def HasVendorPartition(target_files_zip):
603 try:
604 target_files_zip.getinfo("VENDOR/")
605 return True
606 except KeyError:
607 return False
608
Tao Bao457cbf62017-03-06 09:56:01 -0800609
Tao Bao481bab82017-12-21 11:23:09 -0800610def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700611 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800612 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700613
614
Tao Bao481bab82017-12-21 11:23:09 -0800615def WriteFingerprintAssertion(script, target_info, source_info):
616 source_oem_props = source_info.oem_props
617 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700618
Tao Bao481bab82017-12-21 11:23:09 -0800619 if source_oem_props is None and target_oem_props is None:
620 script.AssertSomeFingerprint(
621 source_info.fingerprint, target_info.fingerprint)
622 elif source_oem_props is not None and target_oem_props is not None:
623 script.AssertSomeThumbprint(
624 target_info.GetBuildProp("ro.build.thumbprint"),
625 source_info.GetBuildProp("ro.build.thumbprint"))
626 elif source_oem_props is None and target_oem_props is not None:
627 script.AssertFingerprintOrThumbprint(
628 source_info.fingerprint,
629 target_info.GetBuildProp("ro.build.thumbprint"))
630 else:
631 script.AssertFingerprintOrThumbprint(
632 target_info.fingerprint,
633 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700634
Doug Zongkerfc44a512014-08-26 13:10:25 -0700635
Tao Bao481bab82017-12-21 11:23:09 -0800636def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
637 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700638 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700639
640 Metadata used for on-device compatibility verification is retrieved from
641 target_zip then added to compatibility.zip which is added to the output_zip
642 archive.
643
Tao Baobcd1d162017-08-26 13:10:26 -0700644 Compatibility archive should only be included for devices that have enabled
645 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700646
647 Args:
648 target_zip: Zip file containing the source files to be included for OTA.
649 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800650 target_info: The BuildInfo instance that holds the target build info.
651 source_info: The BuildInfo instance that holds the source build info, if
652 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700653 """
654
Tao Baobcd1d162017-08-26 13:10:26 -0700655 def AddCompatibilityArchive(system_updated, vendor_updated):
656 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700657
Tao Baobcd1d162017-08-26 13:10:26 -0700658 Args:
659 system_updated: If True, the system image will be updated and therefore
660 its metadata should be included.
661 vendor_updated: If True, the vendor image will be updated and therefore
662 its metadata should be included.
663 """
664 # Determine what metadata we need. Files are names relative to META/.
665 compatibility_files = []
666 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
667 system_metadata = ("system_manifest.xml", "system_matrix.xml")
668 if vendor_updated:
669 compatibility_files += vendor_metadata
670 if system_updated:
671 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700672
Tao Baobcd1d162017-08-26 13:10:26 -0700673 # Create new archive.
674 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800675 compatibility_archive_zip = zipfile.ZipFile(
676 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700677
Tao Baobcd1d162017-08-26 13:10:26 -0700678 # Add metadata.
679 for file_name in compatibility_files:
680 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700681
Tao Baobcd1d162017-08-26 13:10:26 -0700682 if target_file_name in target_zip.namelist():
683 data = target_zip.read(target_file_name)
684 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700685
Tao Baobcd1d162017-08-26 13:10:26 -0700686 # Ensure files are written before we copy into output_zip.
687 compatibility_archive_zip.close()
688
689 # Only add the archive if we have any compatibility info.
690 if compatibility_archive_zip.namelist():
691 common.ZipWrite(output_zip, compatibility_archive.name,
692 arcname="compatibility.zip",
693 compress_type=zipfile.ZIP_STORED)
694
695 # Will only proceed if the target has enabled the Treble support (as well as
696 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800697 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700698 return
699
Tao Baobcd1d162017-08-26 13:10:26 -0700700 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800701 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700702 AddCompatibilityArchive(True, True)
703 return
704
Tao Bao481bab82017-12-21 11:23:09 -0800705 source_fp = source_info.fingerprint
706 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700707 system_updated = source_fp != target_fp
708
Tao Bao481bab82017-12-21 11:23:09 -0800709 source_fp_vendor = source_info.GetVendorBuildProp(
710 "ro.vendor.build.fingerprint")
711 target_fp_vendor = target_info.GetVendorBuildProp(
712 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700713 vendor_updated = source_fp_vendor != target_fp_vendor
714
715 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700716
717
Tao Bao491d7e22018-02-21 13:17:22 -0800718def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800719 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700720
Tao Bao481bab82017-12-21 11:23:09 -0800721 # We don't know what version it will be installed on top of. We expect the API
722 # just won't change very often. Similarly for fstab, it might have changed in
723 # the target build.
724 target_api_version = target_info["recovery_api_version"]
725 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700726
Tao Bao481bab82017-12-21 11:23:09 -0800727 if target_info.oem_props and not OPTIONS.oem_no_mount:
728 target_info.WriteMountOemScript(script)
729
Tao Baodf3a48b2018-01-10 16:30:43 -0800730 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700731
Tao Bao491d7e22018-02-21 13:17:22 -0800732 if not OPTIONS.no_signing:
733 staging_file = common.MakeTempFile(suffix='.zip')
734 else:
735 staging_file = output_file
736
737 output_zip = zipfile.ZipFile(
738 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
739
Doug Zongker05d3dea2009-06-22 11:32:31 -0700740 device_specific = common.DeviceSpecificParams(
741 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800742 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700743 output_zip=output_zip,
744 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700745 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700746 metadata=metadata,
747 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700748
Tao Bao457cbf62017-03-06 09:56:01 -0800749 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800750
Tao Bao481bab82017-12-21 11:23:09 -0800751 # Assertions (e.g. downgrade check, device properties check).
752 ts = target_info.GetBuildProp("ro.build.date.utc")
753 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700754 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700755
Tao Bao481bab82017-12-21 11:23:09 -0800756 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700757 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800758
759 # Two-step package strategy (in chronological order, which is *not*
760 # the order in which the generated script has things):
761 #
762 # if stage is not "2/3" or "3/3":
763 # write recovery image to boot partition
764 # set stage to "2/3"
765 # reboot to boot partition and restart recovery
766 # else if stage is "2/3":
767 # write recovery image to recovery partition
768 # set stage to "3/3"
769 # reboot to recovery partition and restart recovery
770 # else:
771 # (stage must be "3/3")
772 # set stage to ""
773 # do normal full package installation:
774 # wipe and install system, boot image, etc.
775 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700776 # complete script normally
777 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800778
779 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
780 OPTIONS.input_tmp, "RECOVERY")
781 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800782 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800783 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800784 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800785 assert fs.fs_type.upper() == "EMMC", \
786 "two-step packages only supported on devices with EMMC /misc partitions"
787 bcb_dev = {"bcb_dev": fs.device}
788 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
789 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700790if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800791""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800792
793 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
794 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800795 script.WriteRawImage("/recovery", "recovery.img")
796 script.AppendExtra("""
797set_stage("%(bcb_dev)s", "3/3");
798reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700799else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800800""" % bcb_dev)
801
Tao Baod42e97e2016-11-30 12:11:57 -0800802 # Stage 3/3: Make changes.
803 script.Comment("Stage 3/3")
804
Tao Bao6c55a8a2015-04-08 15:30:27 -0700805 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800806 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700807
Doug Zongkere5ff5902012-01-17 10:55:37 -0800808 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700809
Doug Zongker01ce19c2014-02-04 13:48:15 -0800810 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700811
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700812 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800813 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700814 if HasVendorPartition(input_zip):
815 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700816
Doug Zongker4b9596f2014-06-09 14:15:45 -0700817 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800818
Tao Baoe709b092018-02-07 12:40:00 -0800819 # See the notes in WriteBlockIncrementalOTAPackage().
820 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
821
Tao Bao457cbf62017-03-06 09:56:01 -0800822 # Full OTA is done as an "incremental" against an empty source image. This
823 # has the effect of writing new data from the package to the entire
824 # partition, but lets us reuse the updater code that writes incrementals to
825 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800826 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
827 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800828 system_tgt.ResetFileMap()
829 system_diff = common.BlockDifference("system", system_tgt, src=None)
Tao Bao76def242017-11-21 09:25:31 -0800830 system_diff.WriteScript(script, output_zip,
831 write_verify_script=OPTIONS.verify)
Doug Zongkereef39442009-04-02 12:14:19 -0700832
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700833 boot_img = common.GetBootableImage(
834 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800835
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700836 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700837 script.ShowProgress(0.1, 0)
838
Tao Baoe709b092018-02-07 12:40:00 -0800839 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
840 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800841 vendor_tgt.ResetFileMap()
842 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Tao Bao76def242017-11-21 09:25:31 -0800843 vendor_diff.WriteScript(script, output_zip,
844 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700845
Tao Bao481bab82017-12-21 11:23:09 -0800846 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700847
Tao Bao481bab82017-12-21 11:23:09 -0800848 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700849 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700850
Doug Zongker01ce19c2014-02-04 13:48:15 -0800851 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700852 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700853
Doug Zongker01ce19c2014-02-04 13:48:15 -0800854 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700855 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700856
Doug Zongker1c390a22009-05-14 19:06:36 -0700857 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700858 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700859
Doug Zongker14833602010-02-02 13:12:04 -0800860 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800861
Doug Zongker922206e2014-03-04 13:16:24 -0800862 if OPTIONS.wipe_user_data:
863 script.ShowProgress(0.1, 10)
864 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700865
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800866 if OPTIONS.two_step:
867 script.AppendExtra("""
868set_stage("%(bcb_dev)s", "");
869""" % bcb_dev)
870 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800871
872 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
873 script.Comment("Stage 1/3")
874 _WriteRecoveryImageToBoot(script, output_zip)
875
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800876 script.AppendExtra("""
877set_stage("%(bcb_dev)s", "2/3");
878reboot_now("%(bcb_dev)s", "");
879endif;
880endif;
881""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800882
Tao Bao5d182562016-02-23 11:38:39 -0800883 script.SetProgress(1)
884 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800885 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800886
887 # We haven't written the metadata entry, which will be done in
888 # FinalizeMetadata.
889 common.ZipClose(output_zip)
890
891 needed_property_files = (
892 NonAbOtaPropertyFiles(),
893 )
894 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700895
Doug Zongkerfc44a512014-08-26 13:10:25 -0700896
Doug Zongker2ea21062010-04-28 16:05:21 -0700897def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800898 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
899 common.ZipWriteStr(output_zip, METADATA_NAME, value,
900 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700901
Doug Zongkerfc44a512014-08-26 13:10:25 -0700902
Tao Bao481bab82017-12-21 11:23:09 -0800903def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800904 # Only incremental OTAs are allowed to reach here.
905 assert OPTIONS.incremental_source is not None
906
Tao Bao481bab82017-12-21 11:23:09 -0800907 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
908 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800909 is_downgrade = long(post_timestamp) < long(pre_timestamp)
910
911 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800912 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700913 raise RuntimeError(
914 "--downgrade or --override_timestamp specified but no downgrade "
915 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800916 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800917 else:
918 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700919 raise RuntimeError(
920 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
921 "Need to specify --override_timestamp OR --downgrade to allow "
922 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800923
924
Tao Baodf3a48b2018-01-10 16:30:43 -0800925def GetPackageMetadata(target_info, source_info=None):
926 """Generates and returns the metadata dict.
927
928 It generates a dict() that contains the info to be written into an OTA
929 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700930 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800931
932 Args:
933 target_info: The BuildInfo instance that holds the target build info.
934 source_info: The BuildInfo instance that holds the source build info, or
935 None if generating full OTA.
936
937 Returns:
938 A dict to be written into package metadata entry.
939 """
940 assert isinstance(target_info, BuildInfo)
941 assert source_info is None or isinstance(source_info, BuildInfo)
942
943 metadata = {
944 'post-build' : target_info.fingerprint,
945 'post-build-incremental' : target_info.GetBuildProp(
946 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800947 'post-sdk-level' : target_info.GetBuildProp(
948 'ro.build.version.sdk'),
949 'post-security-patch-level' : target_info.GetBuildProp(
950 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800951 }
952
953 if target_info.is_ab:
954 metadata['ota-type'] = 'AB'
955 metadata['ota-required-cache'] = '0'
956 else:
957 metadata['ota-type'] = 'BLOCK'
958
959 if OPTIONS.wipe_user_data:
960 metadata['ota-wipe'] = 'yes'
961
962 is_incremental = source_info is not None
963 if is_incremental:
964 metadata['pre-build'] = source_info.fingerprint
965 metadata['pre-build-incremental'] = source_info.GetBuildProp(
966 'ro.build.version.incremental')
967 metadata['pre-device'] = source_info.device
968 else:
969 metadata['pre-device'] = target_info.device
970
Tao Baofaa8e0b2018-04-12 14:31:43 -0700971 # Use the actual post-timestamp, even for a downgrade case.
972 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
973
974 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800975 if is_incremental:
976 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800977
978 return metadata
979
980
Tao Baod3fc38a2018-03-08 16:09:01 -0800981class PropertyFiles(object):
982 """A class that computes the property-files string for an OTA package.
983
984 A property-files string is a comma-separated string that contains the
985 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
986 can be fetched directly with the package URL along with the offset/size info.
987 These strings can be used for streaming A/B OTAs, or allowing an updater to
988 download package metadata entry directly, without paying the cost of
989 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800990
Tao Baocc8e2662018-03-01 19:30:00 -0800991 Computing the final property-files string requires two passes. Because doing
992 the whole package signing (with signapk.jar) will possibly reorder the ZIP
993 entries, which may in turn invalidate earlier computed ZIP entry offset/size
994 values.
995
996 This class provides functions to be called for each pass. The general flow is
997 as follows.
998
Tao Baod3fc38a2018-03-08 16:09:01 -0800999 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001000 # The first pass, which writes placeholders before doing initial signing.
1001 property_files.Compute()
1002 SignOutput()
1003
1004 # The second pass, by replacing the placeholders with actual data.
1005 property_files.Finalize()
1006 SignOutput()
1007
1008 And the caller can additionally verify the final result.
1009
1010 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001011 """
1012
Tao Baocc8e2662018-03-01 19:30:00 -08001013 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001014 self.name = None
1015 self.required = ()
1016 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001017
Tao Baocc8e2662018-03-01 19:30:00 -08001018 def Compute(self, input_zip):
1019 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001020
Tao Baocc8e2662018-03-01 19:30:00 -08001021 We reserve extra space for the offset and size of the metadata entry itself,
1022 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001023
Tao Baocc8e2662018-03-01 19:30:00 -08001024 Args:
1025 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001026
Tao Baocc8e2662018-03-01 19:30:00 -08001027 Returns:
1028 A string with placeholders for the metadata offset/size info, e.g.
1029 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1030 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001031 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001032
Tao Baod2ce2ed2018-03-16 12:59:42 -07001033 class InsufficientSpaceException(Exception):
1034 pass
1035
Tao Baocc8e2662018-03-01 19:30:00 -08001036 def Finalize(self, input_zip, reserved_length):
1037 """Finalizes a property-files string with actual METADATA offset/size info.
1038
1039 The input ZIP file has been signed, with the ZIP entries in the desired
1040 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1041 the ZIP entry offsets and construct the property-files string with actual
1042 data. Note that during this process, we must pad the property-files string
1043 to the reserved length, so that the METADATA entry size remains the same.
1044 Otherwise the entries' offsets and sizes may change again.
1045
1046 Args:
1047 input_zip: The input ZIP file.
1048 reserved_length: The reserved length of the property-files string during
1049 the call to Compute(). The final string must be no more than this
1050 size.
1051
1052 Returns:
1053 A property-files string including the metadata offset/size info, e.g.
1054 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1055
1056 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001057 InsufficientSpaceException: If the reserved length is insufficient to hold
1058 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001059 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001060 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001061 if len(result) > reserved_length:
1062 raise self.InsufficientSpaceException(
1063 'Insufficient reserved space: reserved={}, actual={}'.format(
1064 reserved_length, len(result)))
1065
Tao Baocc8e2662018-03-01 19:30:00 -08001066 result += ' ' * (reserved_length - len(result))
1067 return result
1068
1069 def Verify(self, input_zip, expected):
1070 """Verifies the input ZIP file contains the expected property-files string.
1071
1072 Args:
1073 input_zip: The input ZIP file.
1074 expected: The property-files string that's computed from Finalize().
1075
1076 Raises:
1077 AssertionError: On finding a mismatch.
1078 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001079 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001080 assert actual == expected, \
1081 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1082
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001083 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1084 """
1085 Constructs the property-files string per request.
1086
1087 Args:
1088 zip_file: The input ZIP file.
1089 reserved_length: The reserved length of the property-files string.
1090
1091 Returns:
1092 A property-files string including the metadata offset/size info, e.g.
1093 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1094 """
Tao Baocc8e2662018-03-01 19:30:00 -08001095
1096 def ComputeEntryOffsetSize(name):
1097 """Computes the zip entry offset and size."""
1098 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001099 offset = info.header_offset
1100 offset += zipfile.sizeFileHeader
1101 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001102 size = info.file_size
1103 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1104
1105 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001106 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001107 for entry in self.required:
1108 tokens.append(ComputeEntryOffsetSize(entry))
1109 for entry in self.optional:
1110 if entry in zip_file.namelist():
1111 tokens.append(ComputeEntryOffsetSize(entry))
1112
1113 # 'META-INF/com/android/metadata' is required. We don't know its actual
1114 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001115 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1116 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1117 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1118 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001119 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001120 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001121 else:
1122 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1123
1124 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001125
Tao Bao85f16982018-03-08 16:28:33 -08001126 def _GetPrecomputed(self, input_zip):
1127 """Computes the additional tokens to be included into the property-files.
1128
1129 This applies to tokens without actual ZIP entries, such as
1130 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1131 that they can download the payload metadata directly with the info.
1132
1133 Args:
1134 input_zip: The input zip file.
1135
1136 Returns:
1137 A list of strings (tokens) to be added to the property-files string.
1138 """
1139 # pylint: disable=no-self-use
1140 # pylint: disable=unused-argument
1141 return []
1142
Tao Baofe5b69a2018-03-02 09:47:43 -08001143
Tao Baod3fc38a2018-03-08 16:09:01 -08001144class StreamingPropertyFiles(PropertyFiles):
1145 """A subclass for computing the property-files for streaming A/B OTAs."""
1146
1147 def __init__(self):
1148 super(StreamingPropertyFiles, self).__init__()
1149 self.name = 'ota-streaming-property-files'
1150 self.required = (
1151 # payload.bin and payload_properties.txt must exist.
1152 'payload.bin',
1153 'payload_properties.txt',
1154 )
1155 self.optional = (
1156 # care_map.txt is available only if dm-verity is enabled.
1157 'care_map.txt',
1158 # compatibility.zip is available only if target supports Treble.
1159 'compatibility.zip',
1160 )
1161
1162
Tao Bao85f16982018-03-08 16:28:33 -08001163class AbOtaPropertyFiles(StreamingPropertyFiles):
1164 """The property-files for A/B OTA that includes payload_metadata.bin info.
1165
1166 Since P, we expose one more token (aka property-file), in addition to the ones
1167 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1168 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1169 doesn't exist as a separate ZIP entry, but can be used to verify if the
1170 payload can be applied on the given device.
1171
1172 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1173 and the newly added 'ota-property-files' in P. The new token will only be
1174 available in 'ota-property-files'.
1175 """
1176
1177 def __init__(self):
1178 super(AbOtaPropertyFiles, self).__init__()
1179 self.name = 'ota-property-files'
1180
1181 def _GetPrecomputed(self, input_zip):
1182 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1183 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1184
1185 @staticmethod
1186 def _GetPayloadMetadataOffsetAndSize(input_zip):
1187 """Computes the offset and size of the payload metadata for a given package.
1188
1189 (From system/update_engine/update_metadata.proto)
1190 A delta update file contains all the deltas needed to update a system from
1191 one specific version to another specific version. The update format is
1192 represented by this struct pseudocode:
1193
1194 struct delta_update_file {
1195 char magic[4] = "CrAU";
1196 uint64 file_format_version;
1197 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1198
1199 // Only present if format_version > 1:
1200 uint32 metadata_signature_size;
1201
1202 // The Bzip2 compressed DeltaArchiveManifest
1203 char manifest[metadata_signature_size];
1204
1205 // The signature of the metadata (from the beginning of the payload up to
1206 // this location, not including the signature itself). This is a
1207 // serialized Signatures message.
1208 char medatada_signature_message[metadata_signature_size];
1209
1210 // Data blobs for files, no specific format. The specific offset
1211 // and length of each data blob is recorded in the DeltaArchiveManifest.
1212 struct {
1213 char data[];
1214 } blobs[];
1215
1216 // These two are not signed:
1217 uint64 payload_signatures_message_size;
1218 char payload_signatures_message[];
1219 };
1220
1221 'payload-metadata.bin' contains all the bytes from the beginning of the
1222 payload, till the end of 'medatada_signature_message'.
1223 """
1224 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001225 payload_offset = payload_info.header_offset
1226 payload_offset += zipfile.sizeFileHeader
1227 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001228 payload_size = payload_info.file_size
1229
1230 with input_zip.open('payload.bin', 'r') as payload_fp:
1231 header_bin = payload_fp.read(24)
1232
1233 # network byte order (big-endian)
1234 header = struct.unpack("!IQQL", header_bin)
1235
1236 # 'CrAU'
1237 magic = header[0]
1238 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1239
1240 manifest_size = header[2]
1241 metadata_signature_size = header[3]
1242 metadata_total = 24 + manifest_size + metadata_signature_size
1243 assert metadata_total < payload_size
1244
1245 return (payload_offset, metadata_total)
1246
1247
Tao Bao491d7e22018-02-21 13:17:22 -08001248class NonAbOtaPropertyFiles(PropertyFiles):
1249 """The property-files for non-A/B OTA.
1250
1251 For non-A/B OTA, the property-files string contains the info for METADATA
1252 entry, with which a system updater can be fetched the package metadata prior
1253 to downloading the entire package.
1254 """
1255
1256 def __init__(self):
1257 super(NonAbOtaPropertyFiles, self).__init__()
1258 self.name = 'ota-property-files'
1259
1260
Tao Baod3fc38a2018-03-08 16:09:01 -08001261def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001262 """Finalizes the metadata and signs an A/B OTA package.
1263
1264 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1265 that contains the offsets and sizes for the ZIP entries. An example
1266 property-files string is as follows.
1267
1268 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1269
1270 OTA server can pass down this string, in addition to the package URL, to the
1271 system update client. System update client can then fetch individual ZIP
1272 entries (ZIP_STORED) directly at the given offset of the URL.
1273
1274 Args:
1275 metadata: The metadata dict for the package.
1276 input_file: The input ZIP filename that doesn't contain the package METADATA
1277 entry yet.
1278 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001279 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001280 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001281
Tao Baod2ce2ed2018-03-16 12:59:42 -07001282 def ComputeAllPropertyFiles(input_file, needed_property_files):
1283 # Write the current metadata entry with placeholders.
1284 with zipfile.ZipFile(input_file) as input_zip:
1285 for property_files in needed_property_files:
1286 metadata[property_files.name] = property_files.Compute(input_zip)
1287 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001288
Tao Baod2ce2ed2018-03-16 12:59:42 -07001289 if METADATA_NAME in namelist:
1290 common.ZipDelete(input_file, METADATA_NAME)
1291 output_zip = zipfile.ZipFile(input_file, 'a')
1292 WriteMetadata(metadata, output_zip)
1293 common.ZipClose(output_zip)
1294
1295 if OPTIONS.no_signing:
1296 return input_file
1297
Tao Bao491d7e22018-02-21 13:17:22 -08001298 prelim_signing = common.MakeTempFile(suffix='.zip')
1299 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001300 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001301
Tao Baod2ce2ed2018-03-16 12:59:42 -07001302 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1303 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1304 for property_files in needed_property_files:
1305 metadata[property_files.name] = property_files.Finalize(
1306 prelim_signing_zip, len(metadata[property_files.name]))
1307
1308 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1309 # entries, as well as padding the entry headers. We do a preliminary signing
1310 # (with an incomplete metadata entry) to allow that to happen. Then compute
1311 # the ZIP entry offsets, write back the final metadata and do the final
1312 # signing.
1313 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1314 try:
1315 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1316 except PropertyFiles.InsufficientSpaceException:
1317 # Even with the preliminary signing, the entry orders may change
1318 # dramatically, which leads to insufficiently reserved space during the
1319 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1320 # preliminary signing works, based on the already ordered ZIP entries, to
1321 # address the issue.
1322 prelim_signing = ComputeAllPropertyFiles(
1323 prelim_signing, needed_property_files)
1324 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001325
1326 # Replace the METADATA entry.
1327 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001328 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001329 WriteMetadata(metadata, output_zip)
1330 common.ZipClose(output_zip)
1331
1332 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001333 if OPTIONS.no_signing:
1334 output_file = prelim_signing
1335 else:
1336 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001337
1338 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001339 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001340 for property_files in needed_property_files:
1341 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001342
1343
Tao Bao491d7e22018-02-21 13:17:22 -08001344def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001345 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1346 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001347
Tao Bao481bab82017-12-21 11:23:09 -08001348 target_api_version = target_info["recovery_api_version"]
1349 source_api_version = source_info["recovery_api_version"]
1350 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001351 print("WARNING: generating edify script for a source that "
1352 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001353
Tao Bao481bab82017-12-21 11:23:09 -08001354 script = edify_generator.EdifyGenerator(
1355 source_api_version, target_info, fstab=source_info["fstab"])
1356
1357 if target_info.oem_props or source_info.oem_props:
1358 if not OPTIONS.oem_no_mount:
1359 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001360
Tao Baodf3a48b2018-01-10 16:30:43 -08001361 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001362
Tao Bao491d7e22018-02-21 13:17:22 -08001363 if not OPTIONS.no_signing:
1364 staging_file = common.MakeTempFile(suffix='.zip')
1365 else:
1366 staging_file = output_file
1367
1368 output_zip = zipfile.ZipFile(
1369 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1370
Geremy Condra36bd3652014-02-06 19:45:10 -08001371 device_specific = common.DeviceSpecificParams(
1372 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001373 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001374 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001375 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001376 output_zip=output_zip,
1377 script=script,
1378 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001379 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001380
Geremy Condra36bd3652014-02-06 19:45:10 -08001381 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001382 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001383 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001384 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001385 updating_boot = (not OPTIONS.two_step and
1386 (source_boot.data != target_boot.data))
1387
Geremy Condra36bd3652014-02-06 19:45:10 -08001388 target_recovery = common.GetBootableImage(
1389 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001390
Tao Baoe709b092018-02-07 12:40:00 -08001391 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1392 # shared blocks (i.e. some blocks will show up in multiple files' block
1393 # list). We can only allocate such shared blocks to the first "owner", and
1394 # disable imgdiff for all later occurrences.
1395 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1396 target_info.get('ext4_share_dup_blocks') == "true")
1397 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1398 allow_shared_blocks)
1399 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
1400 allow_shared_blocks)
Tao Baodd2a5892015-03-12 12:32:37 -07001401
Tao Bao0582cb62017-12-21 11:47:01 -08001402 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001403 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001404 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001405
Tao Baof8acad12016-07-07 09:09:58 -07001406 # Check the first block of the source system partition for remount R/W only
1407 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001408 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001409 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001410 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1411 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1412 # b) the blocks listed in block map may not contain all the bytes for a given
1413 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001414 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001415 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1416 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001417 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001418 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001419 version=blockimgdiff_version,
1420 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001421
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001422 if HasVendorPartition(target_zip):
1423 if not HasVendorPartition(source_zip):
1424 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001425 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1426 allow_shared_blocks)
1427 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip,
1428 allow_shared_blocks)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001429
1430 # Check first block of vendor partition for remount R/W only if
1431 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001432 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001433 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001434 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001435 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001436 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001437 version=blockimgdiff_version,
1438 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001439 else:
1440 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001441
Tao Baobcd1d162017-08-26 13:10:26 -07001442 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001443 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001444
Tao Bao481bab82017-12-21 11:23:09 -08001445 # Assertions (e.g. device properties check).
1446 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001447 device_specific.IncrementalOTA_Assertions()
1448
1449 # Two-step incremental package strategy (in chronological order,
1450 # which is *not* the order in which the generated script has
1451 # things):
1452 #
1453 # if stage is not "2/3" or "3/3":
1454 # do verification on current system
1455 # write recovery image to boot partition
1456 # set stage to "2/3"
1457 # reboot to boot partition and restart recovery
1458 # else if stage is "2/3":
1459 # write recovery image to recovery partition
1460 # set stage to "3/3"
1461 # reboot to recovery partition and restart recovery
1462 # else:
1463 # (stage must be "3/3")
1464 # perform update:
1465 # patch system files, etc.
1466 # force full install of new boot image
1467 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001468 # complete script normally
1469 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001470
1471 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001472 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001473 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001474 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001475 assert fs.fs_type.upper() == "EMMC", \
1476 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001477 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001478 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1479 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001480if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001481""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001482
1483 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1484 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001485 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001486 script.WriteRawImage("/recovery", "recovery.img")
1487 script.AppendExtra("""
1488set_stage("%(bcb_dev)s", "3/3");
1489reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001490else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001491""" % bcb_dev)
1492
Tao Baod42e97e2016-11-30 12:11:57 -08001493 # Stage 1/3: (a) Verify the current system.
1494 script.Comment("Stage 1/3")
1495
Tao Bao6c55a8a2015-04-08 15:30:27 -07001496 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001497 script.Print("Source: {}".format(source_info.fingerprint))
1498 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001499
Geremy Condra36bd3652014-02-06 19:45:10 -08001500 script.Print("Verifying current system...")
1501
1502 device_specific.IncrementalOTA_VerifyBegin()
1503
Tao Bao481bab82017-12-21 11:23:09 -08001504 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001505
Tao Baod8d14be2016-02-04 14:26:02 -08001506 # Check the required cache size (i.e. stashed blocks).
1507 size = []
1508 if system_diff:
1509 size.append(system_diff.required_cache)
1510 if vendor_diff:
1511 size.append(vendor_diff.required_cache)
1512
Geremy Condra36bd3652014-02-06 19:45:10 -08001513 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001514 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001515 d = common.Difference(target_boot, source_boot)
1516 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001517 if d is None:
1518 include_full_boot = True
1519 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1520 else:
1521 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001522
Tao Bao89fbb0f2017-01-10 10:47:58 -08001523 print("boot target: %d source: %d diff: %d" % (
1524 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001525
Tao Bao51216552018-08-26 11:53:15 -07001526 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001527
Tao Bao51216552018-08-26 11:53:15 -07001528 script.PatchPartitionCheck(
1529 "{}:{}:{}:{}".format(
1530 boot_type, boot_device, target_boot.size, target_boot.sha1),
1531 "{}:{}:{}:{}".format(
1532 boot_type, boot_device, source_boot.size, source_boot.sha1))
1533
Tao Baod8d14be2016-02-04 14:26:02 -08001534 size.append(target_boot.size)
1535
1536 if size:
1537 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001538
1539 device_specific.IncrementalOTA_VerifyEnd()
1540
1541 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001542 # Stage 1/3: (b) Write recovery image to /boot.
1543 _WriteRecoveryImageToBoot(script, output_zip)
1544
Geremy Condra36bd3652014-02-06 19:45:10 -08001545 script.AppendExtra("""
1546set_stage("%(bcb_dev)s", "2/3");
1547reboot_now("%(bcb_dev)s", "");
1548else
1549""" % bcb_dev)
1550
Tao Baod42e97e2016-11-30 12:11:57 -08001551 # Stage 3/3: Make changes.
1552 script.Comment("Stage 3/3")
1553
Jesse Zhao75bcea02015-01-06 10:59:53 -08001554 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001555 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001556 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001557 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001558
Geremy Condra36bd3652014-02-06 19:45:10 -08001559 script.Comment("---- start making changes here ----")
1560
1561 device_specific.IncrementalOTA_InstallBegin()
1562
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001563 system_diff.WriteScript(script, output_zip,
Tao Bao76def242017-11-21 09:25:31 -08001564 progress=0.8 if vendor_diff else 0.9,
1565 write_verify_script=OPTIONS.verify)
Tao Bao68658c02015-06-01 13:40:49 -07001566
Doug Zongkerfc44a512014-08-26 13:10:25 -07001567 if vendor_diff:
Tao Bao76def242017-11-21 09:25:31 -08001568 vendor_diff.WriteScript(script, output_zip, progress=0.1,
1569 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001570
1571 if OPTIONS.two_step:
1572 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1573 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001574 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001575
1576 if not OPTIONS.two_step:
1577 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001578 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001579 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001580 script.Print("Installing boot image...")
1581 script.WriteRawImage("/boot", "boot.img")
1582 else:
1583 # Produce the boot image by applying a patch to the current
1584 # contents of the boot partition, and write it back to the
1585 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001586 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001587 script.Print("Patching boot image...")
1588 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001589 script.PatchPartition(
1590 '{}:{}:{}:{}'.format(
1591 boot_type, boot_device, target_boot.size, target_boot.sha1),
1592 '{}:{}:{}:{}'.format(
1593 boot_type, boot_device, source_boot.size, source_boot.sha1),
1594 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001595 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001596 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001597
1598 # Do device-specific installation (eg, write radio image).
1599 device_specific.IncrementalOTA_InstallEnd()
1600
1601 if OPTIONS.extra_script is not None:
1602 script.AppendExtra(OPTIONS.extra_script)
1603
Doug Zongker922206e2014-03-04 13:16:24 -08001604 if OPTIONS.wipe_user_data:
1605 script.Print("Erasing user data...")
1606 script.FormatPartition("/data")
1607
Geremy Condra36bd3652014-02-06 19:45:10 -08001608 if OPTIONS.two_step:
1609 script.AppendExtra("""
1610set_stage("%(bcb_dev)s", "");
1611endif;
1612endif;
1613""" % bcb_dev)
1614
1615 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001616 # For downgrade OTAs, we prefer to use the update-binary in the source
1617 # build that is actually newer than the one in the target build.
1618 if OPTIONS.downgrade:
1619 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1620 else:
1621 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001622 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001623
1624 # We haven't written the metadata entry yet, which will be handled in
1625 # FinalizeMetadata().
1626 common.ZipClose(output_zip)
1627
1628 # Sign the generated zip package unless no_signing is specified.
1629 needed_property_files = (
1630 NonAbOtaPropertyFiles(),
1631 )
1632 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001633
Doug Zongker32b527d2014-03-04 10:03:02 -08001634
Tao Bao15a146a2018-02-21 16:06:59 -08001635def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001636 """Returns a target-files.zip file for generating secondary payload.
1637
1638 Although the original target-files.zip already contains secondary slot
1639 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1640 ones without _other suffix. Note that we cannot instead modify the names in
1641 META/ab_partitions.txt, because there are no matching partitions on device.
1642
1643 For the partitions that don't have secondary images, the ones for primary
1644 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1645 bootloader images in the inactive slot.
1646
1647 Args:
1648 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001649 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001650
1651 Returns:
1652 The filename of the target-files.zip for generating secondary payload.
1653 """
1654 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1655 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1656
Tao Baodba59ee2018-01-09 13:21:02 -08001657 with zipfile.ZipFile(input_file, 'r') as input_zip:
1658 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001659 namelist = input_zip.namelist()
1660
1661 # Additionally unzip 'RADIO/*' if exists.
1662 unzip_pattern = UNZIP_PATTERN[:]
1663 if any([entry.startswith('RADIO/') for entry in namelist]):
1664 unzip_pattern.append('RADIO/*')
1665 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001666
1667 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001668 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1669 if info.filename == 'IMAGES/system_other.img':
1670 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1671
1672 # Primary images and friends need to be skipped explicitly.
1673 elif info.filename in ('IMAGES/system.img',
1674 'IMAGES/system.map'):
1675 pass
1676
Tao Bao15a146a2018-02-21 16:06:59 -08001677 # Skip copying the postinstall config if requested.
1678 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1679 pass
1680
Tao Bao12489802018-07-12 14:47:38 -07001681 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001682 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1683
Tao Baof7140c02018-01-30 17:09:24 -08001684 common.ZipClose(target_zip)
1685
1686 return target_file
1687
1688
Tao Bao15a146a2018-02-21 16:06:59 -08001689def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1690 """Returns a target-files.zip that's not containing postinstall_config.txt.
1691
1692 This allows brillo_update_payload script to skip writing all the postinstall
1693 hooks in the generated payload. The input target-files.zip file will be
1694 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1695 contain the postinstall_config.txt entry, the input file will be returned.
1696
1697 Args:
1698 input_file: The input target-files.zip filename.
1699
1700 Returns:
1701 The filename of target-files.zip that doesn't contain postinstall config.
1702 """
1703 # We should only make a copy if postinstall_config entry exists.
1704 with zipfile.ZipFile(input_file, 'r') as input_zip:
1705 if POSTINSTALL_CONFIG not in input_zip.namelist():
1706 return input_file
1707
1708 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1709 shutil.copyfile(input_file, target_file)
1710 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1711 return target_file
1712
1713
Tao Baoc098e9e2016-01-07 13:03:56 -08001714def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1715 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001716 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001717 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001718 if not OPTIONS.no_signing:
1719 staging_file = common.MakeTempFile(suffix='.zip')
1720 else:
1721 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001722 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001723 compression=zipfile.ZIP_DEFLATED)
1724
Tao Bao481bab82017-12-21 11:23:09 -08001725 if source_file is not None:
1726 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1727 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1728 else:
1729 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1730 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001731
Tao Bao481bab82017-12-21 11:23:09 -08001732 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001733 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001734
Tao Bao15a146a2018-02-21 16:06:59 -08001735 if OPTIONS.skip_postinstall:
1736 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1737
Tao Bao40b18822018-01-30 18:19:04 -08001738 # Generate payload.
1739 payload = Payload()
1740
1741 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001742 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001743 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001744 else:
1745 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001746 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001747
Tao Bao40b18822018-01-30 18:19:04 -08001748 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001749
Tao Bao40b18822018-01-30 18:19:04 -08001750 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001751 payload_signer = PayloadSigner()
1752 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001753
Tao Bao40b18822018-01-30 18:19:04 -08001754 # Write the payload into output zip.
1755 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001756
Tao Baof7140c02018-01-30 17:09:24 -08001757 # Generate and include the secondary payload that installs secondary images
1758 # (e.g. system_other.img).
1759 if OPTIONS.include_secondary:
1760 # We always include a full payload for the secondary slot, even when
1761 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001762 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1763 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001764 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001765 secondary_payload.Generate(secondary_target_file,
1766 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001767 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001768 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001769
Tianjie Xucfa86222016-03-07 16:31:19 -08001770 # If dm-verity is supported for the device, copy contents of care_map
1771 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001772 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001773 if (target_info.get("verity") == "true" or
1774 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001775 care_map_path = "META/care_map.txt"
1776 namelist = target_zip.namelist()
1777 if care_map_path in namelist:
1778 care_map_data = target_zip.read(care_map_path)
Tao Bao40b18822018-01-30 18:19:04 -08001779 # In order to support streaming, care_map.txt needs to be packed as
1780 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001781 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001782 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001783 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001784 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001785
Tao Baobcd1d162017-08-26 13:10:26 -07001786 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001787 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001788
Tao Bao21803d32017-04-19 10:16:09 -07001789 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001790
Tao Baofe5b69a2018-03-02 09:47:43 -08001791 # We haven't written the metadata entry yet, which will be handled in
1792 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001793 common.ZipClose(output_zip)
1794
Tao Bao85f16982018-03-08 16:28:33 -08001795 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1796 # all the info of the latter. However, system updaters and OTA servers need to
1797 # take time to switch to the new flag. We keep both of the flags for
1798 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001799 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001800 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001801 StreamingPropertyFiles(),
1802 )
1803 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001804
Tao Baoc098e9e2016-01-07 13:03:56 -08001805
Doug Zongkereef39442009-04-02 12:14:19 -07001806def main(argv):
1807
1808 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001809 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001810 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001811 elif o in ("-i", "--incremental_from"):
1812 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001813 elif o == "--full_radio":
1814 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001815 elif o == "--full_bootloader":
1816 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001817 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001818 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001819 elif o == "--downgrade":
1820 OPTIONS.downgrade = True
1821 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001822 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001823 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001824 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001825 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001826 elif o == "--oem_no_mount":
1827 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001828 elif o in ("-e", "--extra_script"):
1829 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001830 elif o in ("-t", "--worker_threads"):
1831 if a.isdigit():
1832 OPTIONS.worker_threads = int(a)
1833 else:
1834 raise ValueError("Cannot parse value %r for option %r - only "
1835 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001836 elif o in ("-2", "--two_step"):
1837 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001838 elif o == "--include_secondary":
1839 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001840 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001841 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001842 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001843 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001844 elif o == "--block":
1845 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001846 elif o in ("-b", "--binary"):
1847 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001848 elif o == "--stash_threshold":
1849 try:
1850 OPTIONS.stash_threshold = float(a)
1851 except ValueError:
1852 raise ValueError("Cannot parse value %r for option %r - expecting "
1853 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001854 elif o == "--log_diff":
1855 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001856 elif o == "--payload_signer":
1857 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001858 elif o == "--payload_signer_args":
1859 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001860 elif o == "--extracted_input_target_files":
1861 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001862 elif o == "--skip_postinstall":
1863 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001864 else:
1865 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001866 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001867
1868 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001869 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001870 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001871 "package_key=",
1872 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001873 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001874 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001875 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001876 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001877 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001878 "extra_script=",
1879 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001880 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001881 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001882 "no_signing",
1883 "block",
1884 "binary=",
1885 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001886 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001887 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001888 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001889 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001890 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001891 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001892 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001893 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001894 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001895
1896 if len(args) != 2:
1897 common.Usage(__doc__)
1898 sys.exit(1)
1899
Tao Bao5d182562016-02-23 11:38:39 -08001900 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08001901 # We should only allow downgrading incrementals (as opposed to full).
1902 # Otherwise the device may go back from arbitrary build with this full
1903 # OTA package.
1904 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001905 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001906
Tao Bao2db13852018-01-08 22:28:57 -08001907 # Load the build info dicts from the zip directly or the extracted input
1908 # directory. We don't need to unzip the entire target-files zips, because they
1909 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1910 # When loading the info dicts, we don't need to provide the second parameter
1911 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1912 # some properties with their actual paths, such as 'selinux_fc',
1913 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001914 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001915 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001916 else:
Tao Bao2db13852018-01-08 22:28:57 -08001917 with zipfile.ZipFile(args[0], 'r') as input_zip:
1918 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001919
Tao Bao2db13852018-01-08 22:28:57 -08001920 if OPTIONS.verbose:
1921 print("--- target info ---")
1922 common.DumpInfoDict(OPTIONS.info_dict)
1923
1924 # Load the source build dict if applicable.
1925 if OPTIONS.incremental_source is not None:
1926 OPTIONS.target_info_dict = OPTIONS.info_dict
1927 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1928 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1929
1930 if OPTIONS.verbose:
1931 print("--- source info ---")
1932 common.DumpInfoDict(OPTIONS.source_info_dict)
1933
1934 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001935 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1936
Tao Baoc098e9e2016-01-07 13:03:56 -08001937 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1938
Christian Oderf63e2cd2017-05-01 22:30:15 +02001939 # Use the default key to sign the package if not specified with package_key.
1940 # package_keys are needed on ab_updates, so always define them if an
1941 # ab_update is getting created.
1942 if not OPTIONS.no_signing or ab_update:
1943 if OPTIONS.package_key is None:
1944 OPTIONS.package_key = OPTIONS.info_dict.get(
1945 "default_system_dev_certificate",
1946 "build/target/product/security/testkey")
1947 # Get signing keys
1948 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1949
Tao Baoc098e9e2016-01-07 13:03:56 -08001950 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001951 WriteABOTAPackageWithBrilloScript(
1952 target_file=args[0],
1953 output_file=args[1],
1954 source_file=OPTIONS.incremental_source)
1955
Tao Bao89fbb0f2017-01-10 10:47:58 -08001956 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001957 return
1958
Tao Bao2db13852018-01-08 22:28:57 -08001959 # Sanity check the loaded info dicts first.
1960 if OPTIONS.info_dict.get("no_recovery") == "true":
1961 raise common.ExternalError(
1962 "--- target build has specified no recovery ---")
1963
1964 # Non-A/B OTAs rely on /cache partition to store temporary files.
1965 cache_size = OPTIONS.info_dict.get("cache_size")
1966 if cache_size is None:
1967 print("--- can't determine the cache partition size ---")
1968 OPTIONS.cache_size = cache_size
1969
Doug Zongker1c390a22009-05-14 19:06:36 -07001970 if OPTIONS.extra_script is not None:
1971 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1972
Dan Willemsencea5cd22017-03-21 14:44:27 -07001973 if OPTIONS.extracted_input is not None:
1974 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001975 else:
1976 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001977 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001978 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001979
Tao Bao2db13852018-01-08 22:28:57 -08001980 # If the caller explicitly specified the device-specific extensions path via
1981 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1982 # is present in the target target_files. Otherwise, take the path of the file
1983 # from 'tool_extensions' in the info dict and look for that in the local
1984 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001985 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001986 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1987 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001988 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001989 OPTIONS.device_specific = from_input
1990 else:
Tao Bao2db13852018-01-08 22:28:57 -08001991 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001992
Doug Zongker37974732010-09-16 17:44:38 -07001993 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001994 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001995
Tao Bao767e3ac2015-11-10 12:19:19 -08001996 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001997 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08001998 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08001999 WriteFullOTAPackage(
2000 input_zip,
2001 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002002
Tao Bao32b80dc2018-01-08 22:50:47 -08002003 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002004 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002005 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002006 OPTIONS.source_tmp = common.UnzipTemp(
2007 OPTIONS.incremental_source, UNZIP_PATTERN)
2008 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2009 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002010 WriteBlockIncrementalOTAPackage(
2011 input_zip,
2012 source_zip,
2013 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002014
2015 if OPTIONS.log_diff:
2016 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002017 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002018 target_files_diff.recursiveDiff(
2019 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002020
Tao Bao89fbb0f2017-01-10 10:47:58 -08002021 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002022
2023
2024if __name__ == '__main__':
2025 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002026 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002027 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002028 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002029 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002030 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002031 finally:
2032 common.Cleanup()