blob: 1e2f39eb63956436b65c85613f7cb682444252c3 [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"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongkerafb32ea2011-09-22 10:28:04 -070024 -k (--package_key) <key> Key to use to sign the package (default is
25 the value of default_system_dev_certificate from the input
26 target-files's META/misc_info.txt, or
27 "build/target/product/security/testkey" if that value is not
28 specified).
29
30 For incremental OTAs, the default value is based on the source
31 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070032
33 -i (--incremental_from) <file>
34 Generate an incremental OTA using the given target-files zip as
35 the starting build.
36
Tao Bao43078aa2015-04-21 14:32:35 -070037 --full_radio
38 When generating an incremental OTA, always include a full copy of
39 radio image. This option is only meaningful when -i is specified,
40 because a full radio is always included in a full OTA if applicable.
41
leozwangaa6c1a12015-08-14 10:57:58 -070042 --full_bootloader
43 Similar to --full_radio. When generating an incremental OTA, always
44 include a full copy of bootloader image.
45
Tao Baoedb35b82017-10-30 16:07:13 -070046 --verify
47 Remount and verify the checksums of the files written to the system and
48 vendor (if used) partitions. Non-A/B incremental OTAs only.
Michael Runge63f01de2014-10-28 19:24:19 -070049
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080050 -o (--oem_settings) <main_file[,additional_files...]>
51 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -080052 properties on the OEM partition of the intended device. Multiple expected
53 values can be used by providing multiple files. Only the first dict will
54 be used to compute fingerprint, while the rest will be used to assert
55 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080056
Tao Bao8608cde2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Tao Bao337633f2017-12-06 15:20:19 -080063 --wipe_user_data
Doug Zongkerdbfaae52009-04-21 17:12:54 -070064 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Tao Bao5d182562016-02-23 11:38:39 -080067 --downgrade
68 Intentionally generate an incremental OTA that updates from a newer
69 build to an older one (based on timestamp comparison). "post-timestamp"
70 will be replaced by "ota-downgrade=yes" in the metadata file. A data
71 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080072 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080073 the OTA package, unless --binary flag is specified. Please also check the
74 doc for --override_timestamp below.
75
76 --override_timestamp
77 Intentionally generate an incremental OTA that updates from a newer
78 build to an older one (based on timestamp comparison), by overriding the
79 timestamp in package metadata. This differs from --downgrade flag: we
80 know for sure this is NOT an actual downgrade case, but two builds are
81 cut in a reverse order. A legit use case is that we cut a new build C
82 (after having A and B), but want to enfore an update path of A -> C -> B.
83 Specifying --downgrade may not help since that would enforce a data wipe
84 for C -> B update. The value of "post-timestamp" will be set to the newer
85 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080086
Doug Zongker1c390a22009-05-14 19:06:36 -070087 -e (--extra_script) <file>
88 Insert the contents of file at the end of the update script.
89
Doug Zongker9b23f2c2013-11-25 14:44:12 -080090 -2 (--two_step)
91 Generate a 'two-step' OTA package, where recovery is updated
92 first, so that any changes made to the system partition are done
93 using the new recovery (new kernel, etc.).
94
Tao Baof7140c02018-01-30 17:09:24 -080095 --include_secondary
96 Additionally include the payload for secondary slot images (default:
97 False). Only meaningful when generating A/B OTAs.
98
99 By default, an A/B OTA package doesn't contain the images for the
100 secondary slot (e.g. system_other.img). Specifying this flag allows
101 generating a separate payload that will install secondary slot images.
102
103 Such a package needs to be applied in a two-stage manner, with a reboot
104 in-between. During the first stage, the updater applies the primary
105 payload only. Upon finishing, it reboots the device into the newly updated
106 slot. It then continues to install the secondary payload to the inactive
107 slot, but without switching the active slot at the end (needs the matching
108 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
109
110 Due to the special install procedure, the secondary payload will be always
111 generated as a full payload.
112
Doug Zongker26e66192014-02-20 13:22:07 -0800113 --block
Tao Bao457cbf62017-03-06 09:56:01 -0800114 Generate a block-based OTA for non-A/B device. We have deprecated the
115 support for file-based OTA since O. Block-based OTA will be used by
116 default for all non-A/B devices. Keeping this flag here to not break
117 existing callers.
Doug Zongker26e66192014-02-20 13:22:07 -0800118
Doug Zongker25568482014-03-03 10:21:27 -0800119 -b (--binary) <file>
120 Use the given binary as the update-binary in the output package,
121 instead of the binary in the build's target_files. Use for
122 development only.
123
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200124 -t (--worker_threads) <int>
125 Specifies the number of worker-threads that will be used when
126 generating patches for incremental updates (defaults to 3).
127
Tao Bao8dcf7382015-05-21 14:09:49 -0700128 --stash_threshold <float>
129 Specifies the threshold that will be used to compute the maximum
130 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800131
Tao Baod62c6032015-11-30 09:40:20 -0800132 --log_diff <file>
133 Generate a log file that shows the differences in the source and target
134 builds for an incremental package. This option is only meaningful when
135 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700136
137 --payload_signer <signer>
138 Specify the signer when signing the payload and metadata for A/B OTAs.
139 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
140 with the package private key. If the private key cannot be accessed
141 directly, a payload signer that knows how to do that should be specified.
142 The signer will be supplied with "-inkey <path_to_key>",
143 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700144
145 --payload_signer_args <args>
146 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800147
148 --skip_postinstall
149 Skip the postinstall hooks when generating an A/B OTA package (default:
150 False). Note that this discards ALL the hooks, including non-optional
151 ones. Should only be used if caller knows it's safe to do so (e.g. all the
152 postinstall work is to dexopt apps and a data wipe will happen immediately
153 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700154"""
155
Tao Bao89fbb0f2017-01-10 10:47:58 -0800156from __future__ import print_function
157
Doug Zongkerfc44a512014-08-26 13:10:25 -0700158import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800159import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700160import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800161import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800162import struct
Tao Bao481bab82017-12-21 11:23:09 -0800163import subprocess
164import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700165import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700166import zipfile
167
168import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700169import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -0700170
Tao Bao481bab82017-12-21 11:23:09 -0800171if sys.hexversion < 0x02070000:
172 print("Python 2.7 or newer is required.", file=sys.stderr)
173 sys.exit(1)
174
175
Doug Zongkereef39442009-04-02 12:14:19 -0700176OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700177OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700178OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700179OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700180OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700181OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800182OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800183OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700184OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700185OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
186if OPTIONS.worker_threads == 0:
187 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800188OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800189OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900190OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800191OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800192OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700193OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800194OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700195OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700196OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700197# Stash size cannot exceed cache_size * threshold.
198OPTIONS.cache_size = None
199OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800200OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700201OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700202OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700203OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200204OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800205OPTIONS.skip_postinstall = False
206
Tao Bao8dcf7382015-05-21 14:09:49 -0700207
Tao Bao2dd1c482017-02-03 16:49:39 -0800208METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800209POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800210UNZIP_PATTERN = ['IMAGES/*', 'META/*']
211
Tao Bao2dd1c482017-02-03 16:49:39 -0800212
Tao Bao481bab82017-12-21 11:23:09 -0800213class BuildInfo(object):
214 """A class that holds the information for a given build.
215
216 This class wraps up the property querying for a given source or target build.
217 It abstracts away the logic of handling OEM-specific properties, and caches
218 the commonly used properties such as fingerprint.
219
220 There are two types of info dicts: a) build-time info dict, which is generated
221 at build time (i.e. included in a target_files zip); b) OEM info dict that is
222 specified at package generation time (via command line argument
223 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
224 having "oem_fingerprint_properties" in build-time info dict), all the queries
225 would be answered based on build-time info dict only. Otherwise if using
226 OEM-specific properties, some of them will be calculated from two info dicts.
227
228 Users can query properties similarly as using a dict() (e.g. info['fstab']),
229 or to query build properties via GetBuildProp() or GetVendorBuildProp().
230
231 Attributes:
232 info_dict: The build-time info dict.
233 is_ab: Whether it's a build that uses A/B OTA.
234 oem_dicts: A list of OEM dicts.
235 oem_props: A list of OEM properties that should be read from OEM dicts; None
236 if the build doesn't use any OEM-specific property.
237 fingerprint: The fingerprint of the build, which would be calculated based
238 on OEM properties if applicable.
239 device: The device name, which could come from OEM dicts if applicable.
240 """
241
242 def __init__(self, info_dict, oem_dicts):
243 """Initializes a BuildInfo instance with the given dicts.
244
245 Arguments:
246 info_dict: The build-time info dict.
247 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
248 that it always uses the first dict to calculate the fingerprint or the
249 device name. The rest would be used for asserting OEM properties only
250 (e.g. one package can be installed on one of these devices).
251 """
252 self.info_dict = info_dict
253 self.oem_dicts = oem_dicts
254
255 self._is_ab = info_dict.get("ab_update") == "true"
256 self._oem_props = info_dict.get("oem_fingerprint_properties")
257
258 if self._oem_props:
259 assert oem_dicts, "OEM source required for this build"
260
261 # These two should be computed only after setting self._oem_props.
262 self._device = self.GetOemProperty("ro.product.device")
263 self._fingerprint = self.CalculateFingerprint()
264
265 @property
266 def is_ab(self):
267 return self._is_ab
268
269 @property
270 def device(self):
271 return self._device
272
273 @property
274 def fingerprint(self):
275 return self._fingerprint
276
277 @property
278 def oem_props(self):
279 return self._oem_props
280
281 def __getitem__(self, key):
282 return self.info_dict[key]
283
284 def get(self, key, default=None):
285 return self.info_dict.get(key, default)
286
287 def GetBuildProp(self, prop):
288 """Returns the inquired build property."""
289 try:
290 return self.info_dict.get("build.prop", {})[prop]
291 except KeyError:
292 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
293
294 def GetVendorBuildProp(self, prop):
295 """Returns the inquired vendor build property."""
296 try:
297 return self.info_dict.get("vendor.build.prop", {})[prop]
298 except KeyError:
299 raise common.ExternalError(
300 "couldn't find %s in vendor.build.prop" % (prop,))
301
302 def GetOemProperty(self, key):
303 if self.oem_props is not None and key in self.oem_props:
304 return self.oem_dicts[0][key]
305 return self.GetBuildProp(key)
306
307 def CalculateFingerprint(self):
308 if self.oem_props is None:
309 return self.GetBuildProp("ro.build.fingerprint")
310 return "%s/%s/%s:%s" % (
311 self.GetOemProperty("ro.product.brand"),
312 self.GetOemProperty("ro.product.name"),
313 self.GetOemProperty("ro.product.device"),
314 self.GetBuildProp("ro.build.thumbprint"))
315
316 def WriteMountOemScript(self, script):
317 assert self.oem_props is not None
318 recovery_mount_options = self.info_dict.get("recovery_mount_options")
319 script.Mount("/oem", recovery_mount_options)
320
321 def WriteDeviceAssertions(self, script, oem_no_mount):
322 # Read the property directly if not using OEM properties.
323 if not self.oem_props:
324 script.AssertDevice(self.device)
325 return
326
327 # Otherwise assert OEM properties.
328 if not self.oem_dicts:
329 raise common.ExternalError(
330 "No OEM file provided to answer expected assertions")
331
332 for prop in self.oem_props.split():
333 values = []
334 for oem_dict in self.oem_dicts:
335 if prop in oem_dict:
336 values.append(oem_dict[prop])
337 if not values:
338 raise common.ExternalError(
339 "The OEM file is missing the property %s" % (prop,))
340 script.AssertOemProperty(prop, values, oem_no_mount)
341
342
Tao Baofabe0832018-01-17 15:52:28 -0800343class PayloadSigner(object):
344 """A class that wraps the payload signing works.
345
346 When generating a Payload, hashes of the payload and metadata files will be
347 signed with the device key, either by calling an external payload signer or
348 by calling openssl with the package key. This class provides a unified
349 interface, so that callers can just call PayloadSigner.Sign().
350
351 If an external payload signer has been specified (OPTIONS.payload_signer), it
352 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
353 that the signing key should be provided as part of the payload_signer_args.
354 Otherwise without an external signer, it uses the package key
355 (OPTIONS.package_key) and calls openssl for the signing works.
356 """
357
358 def __init__(self):
359 if OPTIONS.payload_signer is None:
360 # Prepare the payload signing key.
361 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
362 pw = OPTIONS.key_passwords[OPTIONS.package_key]
363
364 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
365 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
366 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
367 cmd.extend(["-out", signing_key])
368
369 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
370 stderr=subprocess.STDOUT)
371 stdoutdata, _ = get_signing_key.communicate()
372 assert get_signing_key.returncode == 0, \
373 "Failed to get signing key: {}".format(stdoutdata)
374
375 self.signer = "openssl"
376 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
377 "-pkeyopt", "digest:sha256"]
378 else:
379 self.signer = OPTIONS.payload_signer
380 self.signer_args = OPTIONS.payload_signer_args
381
382 def Sign(self, in_file):
383 """Signs the given input file. Returns the output filename."""
384 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
385 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
386 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
387 stdoutdata, _ = signing.communicate()
388 assert signing.returncode == 0, \
389 "Failed to sign the input file: {}".format(stdoutdata)
390 return out_file
391
392
Tao Bao40b18822018-01-30 18:19:04 -0800393class Payload(object):
394 """Manages the creation and the signing of an A/B OTA Payload."""
395
396 PAYLOAD_BIN = 'payload.bin'
397 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800398 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
399 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800400
Tao Bao667ff572018-02-10 00:02:40 -0800401 def __init__(self, secondary=False):
402 """Initializes a Payload instance.
403
404 Args:
405 secondary: Whether it's generating a secondary payload (default: False).
406 """
Tao Bao40b18822018-01-30 18:19:04 -0800407 # The place where the output from the subprocess should go.
408 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
409 self.payload_file = None
410 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800411 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800412
413 def Generate(self, target_file, source_file=None, additional_args=None):
414 """Generates a payload from the given target-files zip(s).
415
416 Args:
417 target_file: The filename of the target build target-files zip.
418 source_file: The filename of the source build target-files zip; or None if
419 generating a full OTA.
420 additional_args: A list of additional args that should be passed to
421 brillo_update_payload script; or None.
422 """
423 if additional_args is None:
424 additional_args = []
425
426 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
427 cmd = ["brillo_update_payload", "generate",
428 "--payload", payload_file,
429 "--target_image", target_file]
430 if source_file is not None:
431 cmd.extend(["--source_image", source_file])
432 cmd.extend(additional_args)
433 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
434 stdoutdata, _ = p.communicate()
435 assert p.returncode == 0, \
436 "brillo_update_payload generate failed: {}".format(stdoutdata)
437
438 self.payload_file = payload_file
439 self.payload_properties = None
440
441 def Sign(self, payload_signer):
442 """Generates and signs the hashes of the payload and metadata.
443
444 Args:
445 payload_signer: A PayloadSigner() instance that serves the signing work.
446
447 Raises:
448 AssertionError: On any failure when calling brillo_update_payload script.
449 """
450 assert isinstance(payload_signer, PayloadSigner)
451
452 # 1. Generate hashes of the payload and metadata files.
453 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
454 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
455 cmd = ["brillo_update_payload", "hash",
456 "--unsigned_payload", self.payload_file,
457 "--signature_size", "256",
458 "--metadata_hash_file", metadata_sig_file,
459 "--payload_hash_file", payload_sig_file]
460 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
461 p1.communicate()
462 assert p1.returncode == 0, "brillo_update_payload hash failed"
463
464 # 2. Sign the hashes.
465 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
466 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
467
468 # 3. Insert the signatures back into the payload file.
469 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
470 suffix=".bin")
471 cmd = ["brillo_update_payload", "sign",
472 "--unsigned_payload", self.payload_file,
473 "--payload", signed_payload_file,
474 "--signature_size", "256",
475 "--metadata_signature_file", signed_metadata_sig_file,
476 "--payload_signature_file", signed_payload_sig_file]
477 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
478 p1.communicate()
479 assert p1.returncode == 0, "brillo_update_payload sign failed"
480
481 # 4. Dump the signed payload properties.
482 properties_file = common.MakeTempFile(prefix="payload-properties-",
483 suffix=".txt")
484 cmd = ["brillo_update_payload", "properties",
485 "--payload", signed_payload_file,
486 "--properties_file", properties_file]
487 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
488 p1.communicate()
489 assert p1.returncode == 0, "brillo_update_payload properties failed"
490
Tao Bao667ff572018-02-10 00:02:40 -0800491 if self.secondary:
492 with open(properties_file, "a") as f:
493 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
494
Tao Bao40b18822018-01-30 18:19:04 -0800495 if OPTIONS.wipe_user_data:
496 with open(properties_file, "a") as f:
497 f.write("POWERWASH=1\n")
498
499 self.payload_file = signed_payload_file
500 self.payload_properties = properties_file
501
Tao Bao667ff572018-02-10 00:02:40 -0800502 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800503 """Writes the payload to the given zip.
504
505 Args:
506 output_zip: The output ZipFile instance.
507 """
508 assert self.payload_file is not None
509 assert self.payload_properties is not None
510
Tao Bao667ff572018-02-10 00:02:40 -0800511 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800512 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
513 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
514 else:
515 payload_arcname = Payload.PAYLOAD_BIN
516 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
517
Tao Bao40b18822018-01-30 18:19:04 -0800518 # Add the signed payload file and properties into the zip. In order to
519 # support streaming, we pack them as ZIP_STORED. So these entries can be
520 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800521 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800522 compress_type=zipfile.ZIP_STORED)
523 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800524 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800525 compress_type=zipfile.ZIP_STORED)
526
527
Doug Zongkereef39442009-04-02 12:14:19 -0700528def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200529 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700530
Doug Zongker951495f2009-08-14 12:44:19 -0700531 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
532 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700533
534
Tao Bao481bab82017-12-21 11:23:09 -0800535def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800536 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800537 if not oem_source:
538 return None
539
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800540 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800541 for oem_file in oem_source:
542 with open(oem_file) as fp:
543 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800544 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700545
Doug Zongkereef39442009-04-02 12:14:19 -0700546
Tao Baod42e97e2016-11-30 12:11:57 -0800547def _WriteRecoveryImageToBoot(script, output_zip):
548 """Find and write recovery image to /boot in two-step OTA.
549
550 In two-step OTAs, we write recovery image to /boot as the first step so that
551 we can reboot to there and install a new recovery image to /recovery.
552 A special "recovery-two-step.img" will be preferred, which encodes the correct
553 path of "/boot". Otherwise the device may show "device is corrupt" message
554 when booting into /boot.
555
556 Fall back to using the regular recovery.img if the two-step recovery image
557 doesn't exist. Note that rebuilding the special image at this point may be
558 infeasible, because we don't have the desired boot signer and keys when
559 calling ota_from_target_files.py.
560 """
561
562 recovery_two_step_img_name = "recovery-two-step.img"
563 recovery_two_step_img_path = os.path.join(
564 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
565 if os.path.exists(recovery_two_step_img_path):
566 recovery_two_step_img = common.GetBootableImage(
567 recovery_two_step_img_name, recovery_two_step_img_name,
568 OPTIONS.input_tmp, "RECOVERY")
569 common.ZipWriteStr(
570 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800571 print("two-step package: using %s in stage 1/3" % (
572 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800573 script.WriteRawImage("/boot", recovery_two_step_img_name)
574 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800575 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800576 # The "recovery.img" entry has been written into package earlier.
577 script.WriteRawImage("/boot", "recovery.img")
578
579
Doug Zongkerc9253822014-02-04 12:17:58 -0800580def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700581 namelist = [name for name in target_files_zip.namelist()]
582 return ("SYSTEM/recovery-from-boot.p" in namelist or
583 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700584
Tao Bao457cbf62017-03-06 09:56:01 -0800585
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700586def HasVendorPartition(target_files_zip):
587 try:
588 target_files_zip.getinfo("VENDOR/")
589 return True
590 except KeyError:
591 return False
592
Tao Bao457cbf62017-03-06 09:56:01 -0800593
Tao Bao481bab82017-12-21 11:23:09 -0800594def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700595 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800596 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700597
598
Tao Bao481bab82017-12-21 11:23:09 -0800599def WriteFingerprintAssertion(script, target_info, source_info):
600 source_oem_props = source_info.oem_props
601 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700602
Tao Bao481bab82017-12-21 11:23:09 -0800603 if source_oem_props is None and target_oem_props is None:
604 script.AssertSomeFingerprint(
605 source_info.fingerprint, target_info.fingerprint)
606 elif source_oem_props is not None and target_oem_props is not None:
607 script.AssertSomeThumbprint(
608 target_info.GetBuildProp("ro.build.thumbprint"),
609 source_info.GetBuildProp("ro.build.thumbprint"))
610 elif source_oem_props is None and target_oem_props is not None:
611 script.AssertFingerprintOrThumbprint(
612 source_info.fingerprint,
613 target_info.GetBuildProp("ro.build.thumbprint"))
614 else:
615 script.AssertFingerprintOrThumbprint(
616 target_info.fingerprint,
617 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700618
Doug Zongkerfc44a512014-08-26 13:10:25 -0700619
Tao Bao481bab82017-12-21 11:23:09 -0800620def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
621 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700622 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700623
624 Metadata used for on-device compatibility verification is retrieved from
625 target_zip then added to compatibility.zip which is added to the output_zip
626 archive.
627
Tao Baobcd1d162017-08-26 13:10:26 -0700628 Compatibility archive should only be included for devices that have enabled
629 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700630
631 Args:
632 target_zip: Zip file containing the source files to be included for OTA.
633 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800634 target_info: The BuildInfo instance that holds the target build info.
635 source_info: The BuildInfo instance that holds the source build info, if
636 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700637 """
638
Tao Baobcd1d162017-08-26 13:10:26 -0700639 def AddCompatibilityArchive(system_updated, vendor_updated):
640 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700641
Tao Baobcd1d162017-08-26 13:10:26 -0700642 Args:
643 system_updated: If True, the system image will be updated and therefore
644 its metadata should be included.
645 vendor_updated: If True, the vendor image will be updated and therefore
646 its metadata should be included.
647 """
648 # Determine what metadata we need. Files are names relative to META/.
649 compatibility_files = []
650 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
651 system_metadata = ("system_manifest.xml", "system_matrix.xml")
652 if vendor_updated:
653 compatibility_files += vendor_metadata
654 if system_updated:
655 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700656
Tao Baobcd1d162017-08-26 13:10:26 -0700657 # Create new archive.
658 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800659 compatibility_archive_zip = zipfile.ZipFile(
660 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700661
Tao Baobcd1d162017-08-26 13:10:26 -0700662 # Add metadata.
663 for file_name in compatibility_files:
664 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700665
Tao Baobcd1d162017-08-26 13:10:26 -0700666 if target_file_name in target_zip.namelist():
667 data = target_zip.read(target_file_name)
668 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700669
Tao Baobcd1d162017-08-26 13:10:26 -0700670 # Ensure files are written before we copy into output_zip.
671 compatibility_archive_zip.close()
672
673 # Only add the archive if we have any compatibility info.
674 if compatibility_archive_zip.namelist():
675 common.ZipWrite(output_zip, compatibility_archive.name,
676 arcname="compatibility.zip",
677 compress_type=zipfile.ZIP_STORED)
678
679 # Will only proceed if the target has enabled the Treble support (as well as
680 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800681 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700682 return
683
684 # We don't support OEM thumbprint in Treble world (which calculates
685 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800686 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700687
688 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800689 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700690 AddCompatibilityArchive(True, True)
691 return
692
Tao Bao481bab82017-12-21 11:23:09 -0800693 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700694
Tao Bao481bab82017-12-21 11:23:09 -0800695 source_fp = source_info.fingerprint
696 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700697 system_updated = source_fp != target_fp
698
Tao Bao481bab82017-12-21 11:23:09 -0800699 source_fp_vendor = source_info.GetVendorBuildProp(
700 "ro.vendor.build.fingerprint")
701 target_fp_vendor = target_info.GetVendorBuildProp(
702 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700703 vendor_updated = source_fp_vendor != target_fp_vendor
704
705 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700706
707
Tao Bao491d7e22018-02-21 13:17:22 -0800708def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800709 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700710
Tao Bao481bab82017-12-21 11:23:09 -0800711 # We don't know what version it will be installed on top of. We expect the API
712 # just won't change very often. Similarly for fstab, it might have changed in
713 # the target build.
714 target_api_version = target_info["recovery_api_version"]
715 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700716
Tao Bao481bab82017-12-21 11:23:09 -0800717 if target_info.oem_props and not OPTIONS.oem_no_mount:
718 target_info.WriteMountOemScript(script)
719
Tao Baodf3a48b2018-01-10 16:30:43 -0800720 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700721
Tao Bao491d7e22018-02-21 13:17:22 -0800722 if not OPTIONS.no_signing:
723 staging_file = common.MakeTempFile(suffix='.zip')
724 else:
725 staging_file = output_file
726
727 output_zip = zipfile.ZipFile(
728 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
729
Doug Zongker05d3dea2009-06-22 11:32:31 -0700730 device_specific = common.DeviceSpecificParams(
731 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800732 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700733 output_zip=output_zip,
734 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700735 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700736 metadata=metadata,
737 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700738
Tao Bao457cbf62017-03-06 09:56:01 -0800739 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800740
Tao Bao481bab82017-12-21 11:23:09 -0800741 # Assertions (e.g. downgrade check, device properties check).
742 ts = target_info.GetBuildProp("ro.build.date.utc")
743 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700744 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700745
Tao Bao481bab82017-12-21 11:23:09 -0800746 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700747 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800748
749 # Two-step package strategy (in chronological order, which is *not*
750 # the order in which the generated script has things):
751 #
752 # if stage is not "2/3" or "3/3":
753 # write recovery image to boot partition
754 # set stage to "2/3"
755 # reboot to boot partition and restart recovery
756 # else if stage is "2/3":
757 # write recovery image to recovery partition
758 # set stage to "3/3"
759 # reboot to recovery partition and restart recovery
760 # else:
761 # (stage must be "3/3")
762 # set stage to ""
763 # do normal full package installation:
764 # wipe and install system, boot image, etc.
765 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700766 # complete script normally
767 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800768
769 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
770 OPTIONS.input_tmp, "RECOVERY")
771 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800772 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800773 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800774 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800775 assert fs.fs_type.upper() == "EMMC", \
776 "two-step packages only supported on devices with EMMC /misc partitions"
777 bcb_dev = {"bcb_dev": fs.device}
778 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
779 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700780if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800781""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800782
783 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
784 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800785 script.WriteRawImage("/recovery", "recovery.img")
786 script.AppendExtra("""
787set_stage("%(bcb_dev)s", "3/3");
788reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700789else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800790""" % bcb_dev)
791
Tao Baod42e97e2016-11-30 12:11:57 -0800792 # Stage 3/3: Make changes.
793 script.Comment("Stage 3/3")
794
Tao Bao6c55a8a2015-04-08 15:30:27 -0700795 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800796 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700797
Doug Zongkere5ff5902012-01-17 10:55:37 -0800798 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700799
Doug Zongker01ce19c2014-02-04 13:48:15 -0800800 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700801
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700802 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800803 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700804 if HasVendorPartition(input_zip):
805 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700806
Doug Zongker4b9596f2014-06-09 14:15:45 -0700807 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800808
Tao Baoe709b092018-02-07 12:40:00 -0800809 # See the notes in WriteBlockIncrementalOTAPackage().
810 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
811
Tao Bao457cbf62017-03-06 09:56:01 -0800812 # Full OTA is done as an "incremental" against an empty source image. This
813 # has the effect of writing new data from the package to the entire
814 # partition, but lets us reuse the updater code that writes incrementals to
815 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800816 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
817 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800818 system_tgt.ResetFileMap()
819 system_diff = common.BlockDifference("system", system_tgt, src=None)
820 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700821
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700822 boot_img = common.GetBootableImage(
823 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800824
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700825 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700826 script.ShowProgress(0.1, 0)
827
Tao Baoe709b092018-02-07 12:40:00 -0800828 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
829 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800830 vendor_tgt.ResetFileMap()
831 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
832 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700833
Tao Bao481bab82017-12-21 11:23:09 -0800834 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700835
Tao Bao481bab82017-12-21 11:23:09 -0800836 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700837 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700838
Doug Zongker01ce19c2014-02-04 13:48:15 -0800839 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700840 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700841
Doug Zongker01ce19c2014-02-04 13:48:15 -0800842 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700843 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700844
Doug Zongker1c390a22009-05-14 19:06:36 -0700845 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700846 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700847
Doug Zongker14833602010-02-02 13:12:04 -0800848 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800849
Doug Zongker922206e2014-03-04 13:16:24 -0800850 if OPTIONS.wipe_user_data:
851 script.ShowProgress(0.1, 10)
852 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700853
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800854 if OPTIONS.two_step:
855 script.AppendExtra("""
856set_stage("%(bcb_dev)s", "");
857""" % bcb_dev)
858 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800859
860 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
861 script.Comment("Stage 1/3")
862 _WriteRecoveryImageToBoot(script, output_zip)
863
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800864 script.AppendExtra("""
865set_stage("%(bcb_dev)s", "2/3");
866reboot_now("%(bcb_dev)s", "");
867endif;
868endif;
869""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800870
Tao Bao5d182562016-02-23 11:38:39 -0800871 script.SetProgress(1)
872 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800873 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800874
875 # We haven't written the metadata entry, which will be done in
876 # FinalizeMetadata.
877 common.ZipClose(output_zip)
878
879 needed_property_files = (
880 NonAbOtaPropertyFiles(),
881 )
882 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700883
Doug Zongkerfc44a512014-08-26 13:10:25 -0700884
Doug Zongker2ea21062010-04-28 16:05:21 -0700885def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800886 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
887 common.ZipWriteStr(output_zip, METADATA_NAME, value,
888 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700889
Doug Zongkerfc44a512014-08-26 13:10:25 -0700890
Tao Bao481bab82017-12-21 11:23:09 -0800891def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800892 # Only incremental OTAs are allowed to reach here.
893 assert OPTIONS.incremental_source is not None
894
Tao Bao481bab82017-12-21 11:23:09 -0800895 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
896 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800897 is_downgrade = long(post_timestamp) < long(pre_timestamp)
898
899 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800900 if not is_downgrade:
901 raise RuntimeError("--downgrade specified but no downgrade detected: "
902 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800903 metadata["ota-downgrade"] = "yes"
904 elif OPTIONS.timestamp:
905 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800906 raise RuntimeError("--override_timestamp specified but no timestamp hack "
907 "needed: pre: %s, post: %s" % (pre_timestamp,
908 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800909 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800910 else:
911 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800912 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800913 "pre: %s, post: %s. Need to specify "
914 "--override_timestamp OR --downgrade to allow "
915 "building the incremental." % (pre_timestamp,
916 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800917 metadata["post-timestamp"] = post_timestamp
918
919
Tao Baodf3a48b2018-01-10 16:30:43 -0800920def GetPackageMetadata(target_info, source_info=None):
921 """Generates and returns the metadata dict.
922
923 It generates a dict() that contains the info to be written into an OTA
924 package (META-INF/com/android/metadata). It also handles the detection of
925 downgrade / timestamp override / data wipe based on the global options.
926
927 Args:
928 target_info: The BuildInfo instance that holds the target build info.
929 source_info: The BuildInfo instance that holds the source build info, or
930 None if generating full OTA.
931
932 Returns:
933 A dict to be written into package metadata entry.
934 """
935 assert isinstance(target_info, BuildInfo)
936 assert source_info is None or isinstance(source_info, BuildInfo)
937
938 metadata = {
939 'post-build' : target_info.fingerprint,
940 'post-build-incremental' : target_info.GetBuildProp(
941 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800942 'post-sdk-level' : target_info.GetBuildProp(
943 'ro.build.version.sdk'),
944 'post-security-patch-level' : target_info.GetBuildProp(
945 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800946 }
947
948 if target_info.is_ab:
949 metadata['ota-type'] = 'AB'
950 metadata['ota-required-cache'] = '0'
951 else:
952 metadata['ota-type'] = 'BLOCK'
953
954 if OPTIONS.wipe_user_data:
955 metadata['ota-wipe'] = 'yes'
956
957 is_incremental = source_info is not None
958 if is_incremental:
959 metadata['pre-build'] = source_info.fingerprint
960 metadata['pre-build-incremental'] = source_info.GetBuildProp(
961 'ro.build.version.incremental')
962 metadata['pre-device'] = source_info.device
963 else:
964 metadata['pre-device'] = target_info.device
965
966 # Detect downgrades, or fill in the post-timestamp.
967 if is_incremental:
968 HandleDowngradeMetadata(metadata, target_info, source_info)
969 else:
970 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
971
972 return metadata
973
974
Tao Baod3fc38a2018-03-08 16:09:01 -0800975class PropertyFiles(object):
976 """A class that computes the property-files string for an OTA package.
977
978 A property-files string is a comma-separated string that contains the
979 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
980 can be fetched directly with the package URL along with the offset/size info.
981 These strings can be used for streaming A/B OTAs, or allowing an updater to
982 download package metadata entry directly, without paying the cost of
983 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800984
Tao Baocc8e2662018-03-01 19:30:00 -0800985 Computing the final property-files string requires two passes. Because doing
986 the whole package signing (with signapk.jar) will possibly reorder the ZIP
987 entries, which may in turn invalidate earlier computed ZIP entry offset/size
988 values.
989
990 This class provides functions to be called for each pass. The general flow is
991 as follows.
992
Tao Baod3fc38a2018-03-08 16:09:01 -0800993 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800994 # The first pass, which writes placeholders before doing initial signing.
995 property_files.Compute()
996 SignOutput()
997
998 # The second pass, by replacing the placeholders with actual data.
999 property_files.Finalize()
1000 SignOutput()
1001
1002 And the caller can additionally verify the final result.
1003
1004 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001005 """
1006
Tao Baocc8e2662018-03-01 19:30:00 -08001007 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001008 self.name = None
1009 self.required = ()
1010 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001011
Tao Baocc8e2662018-03-01 19:30:00 -08001012 def Compute(self, input_zip):
1013 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001014
Tao Baocc8e2662018-03-01 19:30:00 -08001015 We reserve extra space for the offset and size of the metadata entry itself,
1016 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001017
Tao Baocc8e2662018-03-01 19:30:00 -08001018 Args:
1019 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001020
Tao Baocc8e2662018-03-01 19:30:00 -08001021 Returns:
1022 A string with placeholders for the metadata offset/size info, e.g.
1023 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1024 """
1025 return self._GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001026
Tao Baod2ce2ed2018-03-16 12:59:42 -07001027 class InsufficientSpaceException(Exception):
1028 pass
1029
Tao Baocc8e2662018-03-01 19:30:00 -08001030 def Finalize(self, input_zip, reserved_length):
1031 """Finalizes a property-files string with actual METADATA offset/size info.
1032
1033 The input ZIP file has been signed, with the ZIP entries in the desired
1034 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1035 the ZIP entry offsets and construct the property-files string with actual
1036 data. Note that during this process, we must pad the property-files string
1037 to the reserved length, so that the METADATA entry size remains the same.
1038 Otherwise the entries' offsets and sizes may change again.
1039
1040 Args:
1041 input_zip: The input ZIP file.
1042 reserved_length: The reserved length of the property-files string during
1043 the call to Compute(). The final string must be no more than this
1044 size.
1045
1046 Returns:
1047 A property-files string including the metadata offset/size info, e.g.
1048 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1049
1050 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001051 InsufficientSpaceException: If the reserved length is insufficient to hold
1052 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001053 """
1054 result = self._GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001055 if len(result) > reserved_length:
1056 raise self.InsufficientSpaceException(
1057 'Insufficient reserved space: reserved={}, actual={}'.format(
1058 reserved_length, len(result)))
1059
Tao Baocc8e2662018-03-01 19:30:00 -08001060 result += ' ' * (reserved_length - len(result))
1061 return result
1062
1063 def Verify(self, input_zip, expected):
1064 """Verifies the input ZIP file contains the expected property-files string.
1065
1066 Args:
1067 input_zip: The input ZIP file.
1068 expected: The property-files string that's computed from Finalize().
1069
1070 Raises:
1071 AssertionError: On finding a mismatch.
1072 """
1073 actual = self._GetPropertyFilesString(input_zip)
1074 assert actual == expected, \
1075 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1076
1077 def _GetPropertyFilesString(self, zip_file, reserve_space=False):
1078 """Constructs the property-files string per request."""
1079
1080 def ComputeEntryOffsetSize(name):
1081 """Computes the zip entry offset and size."""
1082 info = zip_file.getinfo(name)
1083 offset = info.header_offset + len(info.FileHeader())
1084 size = info.file_size
1085 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1086
1087 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001088 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001089 for entry in self.required:
1090 tokens.append(ComputeEntryOffsetSize(entry))
1091 for entry in self.optional:
1092 if entry in zip_file.namelist():
1093 tokens.append(ComputeEntryOffsetSize(entry))
1094
1095 # 'META-INF/com/android/metadata' is required. We don't know its actual
1096 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001097 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1098 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1099 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1100 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001101 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001102 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001103 else:
1104 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1105
1106 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001107
Tao Bao85f16982018-03-08 16:28:33 -08001108 def _GetPrecomputed(self, input_zip):
1109 """Computes the additional tokens to be included into the property-files.
1110
1111 This applies to tokens without actual ZIP entries, such as
1112 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1113 that they can download the payload metadata directly with the info.
1114
1115 Args:
1116 input_zip: The input zip file.
1117
1118 Returns:
1119 A list of strings (tokens) to be added to the property-files string.
1120 """
1121 # pylint: disable=no-self-use
1122 # pylint: disable=unused-argument
1123 return []
1124
Tao Baofe5b69a2018-03-02 09:47:43 -08001125
Tao Baod3fc38a2018-03-08 16:09:01 -08001126class StreamingPropertyFiles(PropertyFiles):
1127 """A subclass for computing the property-files for streaming A/B OTAs."""
1128
1129 def __init__(self):
1130 super(StreamingPropertyFiles, self).__init__()
1131 self.name = 'ota-streaming-property-files'
1132 self.required = (
1133 # payload.bin and payload_properties.txt must exist.
1134 'payload.bin',
1135 'payload_properties.txt',
1136 )
1137 self.optional = (
1138 # care_map.txt is available only if dm-verity is enabled.
1139 'care_map.txt',
1140 # compatibility.zip is available only if target supports Treble.
1141 'compatibility.zip',
1142 )
1143
1144
Tao Bao85f16982018-03-08 16:28:33 -08001145class AbOtaPropertyFiles(StreamingPropertyFiles):
1146 """The property-files for A/B OTA that includes payload_metadata.bin info.
1147
1148 Since P, we expose one more token (aka property-file), in addition to the ones
1149 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1150 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1151 doesn't exist as a separate ZIP entry, but can be used to verify if the
1152 payload can be applied on the given device.
1153
1154 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1155 and the newly added 'ota-property-files' in P. The new token will only be
1156 available in 'ota-property-files'.
1157 """
1158
1159 def __init__(self):
1160 super(AbOtaPropertyFiles, self).__init__()
1161 self.name = 'ota-property-files'
1162
1163 def _GetPrecomputed(self, input_zip):
1164 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1165 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1166
1167 @staticmethod
1168 def _GetPayloadMetadataOffsetAndSize(input_zip):
1169 """Computes the offset and size of the payload metadata for a given package.
1170
1171 (From system/update_engine/update_metadata.proto)
1172 A delta update file contains all the deltas needed to update a system from
1173 one specific version to another specific version. The update format is
1174 represented by this struct pseudocode:
1175
1176 struct delta_update_file {
1177 char magic[4] = "CrAU";
1178 uint64 file_format_version;
1179 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1180
1181 // Only present if format_version > 1:
1182 uint32 metadata_signature_size;
1183
1184 // The Bzip2 compressed DeltaArchiveManifest
1185 char manifest[metadata_signature_size];
1186
1187 // The signature of the metadata (from the beginning of the payload up to
1188 // this location, not including the signature itself). This is a
1189 // serialized Signatures message.
1190 char medatada_signature_message[metadata_signature_size];
1191
1192 // Data blobs for files, no specific format. The specific offset
1193 // and length of each data blob is recorded in the DeltaArchiveManifest.
1194 struct {
1195 char data[];
1196 } blobs[];
1197
1198 // These two are not signed:
1199 uint64 payload_signatures_message_size;
1200 char payload_signatures_message[];
1201 };
1202
1203 'payload-metadata.bin' contains all the bytes from the beginning of the
1204 payload, till the end of 'medatada_signature_message'.
1205 """
1206 payload_info = input_zip.getinfo('payload.bin')
1207 payload_offset = payload_info.header_offset + len(payload_info.FileHeader())
1208 payload_size = payload_info.file_size
1209
1210 with input_zip.open('payload.bin', 'r') as payload_fp:
1211 header_bin = payload_fp.read(24)
1212
1213 # network byte order (big-endian)
1214 header = struct.unpack("!IQQL", header_bin)
1215
1216 # 'CrAU'
1217 magic = header[0]
1218 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1219
1220 manifest_size = header[2]
1221 metadata_signature_size = header[3]
1222 metadata_total = 24 + manifest_size + metadata_signature_size
1223 assert metadata_total < payload_size
1224
1225 return (payload_offset, metadata_total)
1226
1227
Tao Bao491d7e22018-02-21 13:17:22 -08001228class NonAbOtaPropertyFiles(PropertyFiles):
1229 """The property-files for non-A/B OTA.
1230
1231 For non-A/B OTA, the property-files string contains the info for METADATA
1232 entry, with which a system updater can be fetched the package metadata prior
1233 to downloading the entire package.
1234 """
1235
1236 def __init__(self):
1237 super(NonAbOtaPropertyFiles, self).__init__()
1238 self.name = 'ota-property-files'
1239
1240
Tao Baod3fc38a2018-03-08 16:09:01 -08001241def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001242 """Finalizes the metadata and signs an A/B OTA package.
1243
1244 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1245 that contains the offsets and sizes for the ZIP entries. An example
1246 property-files string is as follows.
1247
1248 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1249
1250 OTA server can pass down this string, in addition to the package URL, to the
1251 system update client. System update client can then fetch individual ZIP
1252 entries (ZIP_STORED) directly at the given offset of the URL.
1253
1254 Args:
1255 metadata: The metadata dict for the package.
1256 input_file: The input ZIP filename that doesn't contain the package METADATA
1257 entry yet.
1258 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001259 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001260 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001261
Tao Baod2ce2ed2018-03-16 12:59:42 -07001262 def ComputeAllPropertyFiles(input_file, needed_property_files):
1263 # Write the current metadata entry with placeholders.
1264 with zipfile.ZipFile(input_file) as input_zip:
1265 for property_files in needed_property_files:
1266 metadata[property_files.name] = property_files.Compute(input_zip)
1267 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001268
Tao Baod2ce2ed2018-03-16 12:59:42 -07001269 if METADATA_NAME in namelist:
1270 common.ZipDelete(input_file, METADATA_NAME)
1271 output_zip = zipfile.ZipFile(input_file, 'a')
1272 WriteMetadata(metadata, output_zip)
1273 common.ZipClose(output_zip)
1274
1275 if OPTIONS.no_signing:
1276 return input_file
1277
Tao Bao491d7e22018-02-21 13:17:22 -08001278 prelim_signing = common.MakeTempFile(suffix='.zip')
1279 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001280 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001281
Tao Baod2ce2ed2018-03-16 12:59:42 -07001282 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1283 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1284 for property_files in needed_property_files:
1285 metadata[property_files.name] = property_files.Finalize(
1286 prelim_signing_zip, len(metadata[property_files.name]))
1287
1288 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1289 # entries, as well as padding the entry headers. We do a preliminary signing
1290 # (with an incomplete metadata entry) to allow that to happen. Then compute
1291 # the ZIP entry offsets, write back the final metadata and do the final
1292 # signing.
1293 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1294 try:
1295 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1296 except PropertyFiles.InsufficientSpaceException:
1297 # Even with the preliminary signing, the entry orders may change
1298 # dramatically, which leads to insufficiently reserved space during the
1299 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1300 # preliminary signing works, based on the already ordered ZIP entries, to
1301 # address the issue.
1302 prelim_signing = ComputeAllPropertyFiles(
1303 prelim_signing, needed_property_files)
1304 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001305
1306 # Replace the METADATA entry.
1307 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001308 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001309 WriteMetadata(metadata, output_zip)
1310 common.ZipClose(output_zip)
1311
1312 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001313 if OPTIONS.no_signing:
1314 output_file = prelim_signing
1315 else:
1316 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001317
1318 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001319 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001320 for property_files in needed_property_files:
1321 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001322
1323
Tao Bao491d7e22018-02-21 13:17:22 -08001324def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001325 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1326 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001327
Tao Bao481bab82017-12-21 11:23:09 -08001328 target_api_version = target_info["recovery_api_version"]
1329 source_api_version = source_info["recovery_api_version"]
1330 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001331 print("WARNING: generating edify script for a source that "
1332 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001333
Tao Bao481bab82017-12-21 11:23:09 -08001334 script = edify_generator.EdifyGenerator(
1335 source_api_version, target_info, fstab=source_info["fstab"])
1336
1337 if target_info.oem_props or source_info.oem_props:
1338 if not OPTIONS.oem_no_mount:
1339 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001340
Tao Baodf3a48b2018-01-10 16:30:43 -08001341 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001342
Tao Bao491d7e22018-02-21 13:17:22 -08001343 if not OPTIONS.no_signing:
1344 staging_file = common.MakeTempFile(suffix='.zip')
1345 else:
1346 staging_file = output_file
1347
1348 output_zip = zipfile.ZipFile(
1349 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1350
Geremy Condra36bd3652014-02-06 19:45:10 -08001351 device_specific = common.DeviceSpecificParams(
1352 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001353 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001354 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001355 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001356 output_zip=output_zip,
1357 script=script,
1358 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001359 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001360
Geremy Condra36bd3652014-02-06 19:45:10 -08001361 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001362 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001363 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001364 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001365 updating_boot = (not OPTIONS.two_step and
1366 (source_boot.data != target_boot.data))
1367
Geremy Condra36bd3652014-02-06 19:45:10 -08001368 target_recovery = common.GetBootableImage(
1369 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001370
Tao Baoe709b092018-02-07 12:40:00 -08001371 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1372 # shared blocks (i.e. some blocks will show up in multiple files' block
1373 # list). We can only allocate such shared blocks to the first "owner", and
1374 # disable imgdiff for all later occurrences.
1375 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1376 target_info.get('ext4_share_dup_blocks') == "true")
1377 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1378 allow_shared_blocks)
1379 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
1380 allow_shared_blocks)
Tao Baodd2a5892015-03-12 12:32:37 -07001381
Tao Bao0582cb62017-12-21 11:47:01 -08001382 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001383 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001384 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001385
Tao Baof8acad12016-07-07 09:09:58 -07001386 # Check the first block of the source system partition for remount R/W only
1387 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001388 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001389 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001390 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1391 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1392 # b) the blocks listed in block map may not contain all the bytes for a given
1393 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001394 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001395 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1396 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001397 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001398 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001399 version=blockimgdiff_version,
1400 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001401
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001402 if HasVendorPartition(target_zip):
1403 if not HasVendorPartition(source_zip):
1404 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001405 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1406 allow_shared_blocks)
1407 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip,
1408 allow_shared_blocks)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001409
1410 # Check first block of vendor partition for remount R/W only if
1411 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001412 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001413 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001414 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001415 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001416 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001417 version=blockimgdiff_version,
1418 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001419 else:
1420 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001421
Tao Baobcd1d162017-08-26 13:10:26 -07001422 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001423 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001424
Tao Bao481bab82017-12-21 11:23:09 -08001425 # Assertions (e.g. device properties check).
1426 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001427 device_specific.IncrementalOTA_Assertions()
1428
1429 # Two-step incremental package strategy (in chronological order,
1430 # which is *not* the order in which the generated script has
1431 # things):
1432 #
1433 # if stage is not "2/3" or "3/3":
1434 # do verification on current system
1435 # write recovery image to boot partition
1436 # set stage to "2/3"
1437 # reboot to boot partition and restart recovery
1438 # else if stage is "2/3":
1439 # write recovery image to recovery partition
1440 # set stage to "3/3"
1441 # reboot to recovery partition and restart recovery
1442 # else:
1443 # (stage must be "3/3")
1444 # perform update:
1445 # patch system files, etc.
1446 # force full install of new boot image
1447 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001448 # complete script normally
1449 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001450
1451 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001452 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001453 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001454 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001455 assert fs.fs_type.upper() == "EMMC", \
1456 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001457 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001458 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1459 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001460if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001461""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001462
1463 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1464 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001465 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001466 script.WriteRawImage("/recovery", "recovery.img")
1467 script.AppendExtra("""
1468set_stage("%(bcb_dev)s", "3/3");
1469reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001470else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001471""" % bcb_dev)
1472
Tao Baod42e97e2016-11-30 12:11:57 -08001473 # Stage 1/3: (a) Verify the current system.
1474 script.Comment("Stage 1/3")
1475
Tao Bao6c55a8a2015-04-08 15:30:27 -07001476 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001477 script.Print("Source: {}".format(source_info.fingerprint))
1478 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001479
Geremy Condra36bd3652014-02-06 19:45:10 -08001480 script.Print("Verifying current system...")
1481
1482 device_specific.IncrementalOTA_VerifyBegin()
1483
Tao Bao481bab82017-12-21 11:23:09 -08001484 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001485
Tao Baod8d14be2016-02-04 14:26:02 -08001486 # Check the required cache size (i.e. stashed blocks).
1487 size = []
1488 if system_diff:
1489 size.append(system_diff.required_cache)
1490 if vendor_diff:
1491 size.append(vendor_diff.required_cache)
1492
Geremy Condra36bd3652014-02-06 19:45:10 -08001493 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001494 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001495 d = common.Difference(target_boot, source_boot)
1496 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001497 if d is None:
1498 include_full_boot = True
1499 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1500 else:
1501 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001502
Tao Bao89fbb0f2017-01-10 10:47:58 -08001503 print("boot target: %d source: %d diff: %d" % (
1504 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001505
Doug Zongkerf8340082014-08-05 10:39:37 -07001506 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001507
Doug Zongkerf8340082014-08-05 10:39:37 -07001508 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1509 (boot_type, boot_device,
1510 source_boot.size, source_boot.sha1,
1511 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001512 size.append(target_boot.size)
1513
1514 if size:
1515 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001516
1517 device_specific.IncrementalOTA_VerifyEnd()
1518
1519 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001520 # Stage 1/3: (b) Write recovery image to /boot.
1521 _WriteRecoveryImageToBoot(script, output_zip)
1522
Geremy Condra36bd3652014-02-06 19:45:10 -08001523 script.AppendExtra("""
1524set_stage("%(bcb_dev)s", "2/3");
1525reboot_now("%(bcb_dev)s", "");
1526else
1527""" % bcb_dev)
1528
Tao Baod42e97e2016-11-30 12:11:57 -08001529 # Stage 3/3: Make changes.
1530 script.Comment("Stage 3/3")
1531
Jesse Zhao75bcea02015-01-06 10:59:53 -08001532 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001533 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001534 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001535 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001536
Geremy Condra36bd3652014-02-06 19:45:10 -08001537 script.Comment("---- start making changes here ----")
1538
1539 device_specific.IncrementalOTA_InstallBegin()
1540
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001541 system_diff.WriteScript(script, output_zip,
1542 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001543
Doug Zongkerfc44a512014-08-26 13:10:25 -07001544 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001545 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001546
1547 if OPTIONS.two_step:
1548 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1549 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001550 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001551
1552 if not OPTIONS.two_step:
1553 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001554 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001555 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001556 script.Print("Installing boot image...")
1557 script.WriteRawImage("/boot", "boot.img")
1558 else:
1559 # Produce the boot image by applying a patch to the current
1560 # contents of the boot partition, and write it back to the
1561 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001562 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001563 script.Print("Patching boot image...")
1564 script.ShowProgress(0.1, 10)
1565 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1566 % (boot_type, boot_device,
1567 source_boot.size, source_boot.sha1,
1568 target_boot.size, target_boot.sha1),
1569 "-",
1570 target_boot.size, target_boot.sha1,
1571 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001572 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001573 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001574
1575 # Do device-specific installation (eg, write radio image).
1576 device_specific.IncrementalOTA_InstallEnd()
1577
1578 if OPTIONS.extra_script is not None:
1579 script.AppendExtra(OPTIONS.extra_script)
1580
Doug Zongker922206e2014-03-04 13:16:24 -08001581 if OPTIONS.wipe_user_data:
1582 script.Print("Erasing user data...")
1583 script.FormatPartition("/data")
1584
Geremy Condra36bd3652014-02-06 19:45:10 -08001585 if OPTIONS.two_step:
1586 script.AppendExtra("""
1587set_stage("%(bcb_dev)s", "");
1588endif;
1589endif;
1590""" % bcb_dev)
1591
1592 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001593 # For downgrade OTAs, we prefer to use the update-binary in the source
1594 # build that is actually newer than the one in the target build.
1595 if OPTIONS.downgrade:
1596 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1597 else:
1598 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001599 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001600
1601 # We haven't written the metadata entry yet, which will be handled in
1602 # FinalizeMetadata().
1603 common.ZipClose(output_zip)
1604
1605 # Sign the generated zip package unless no_signing is specified.
1606 needed_property_files = (
1607 NonAbOtaPropertyFiles(),
1608 )
1609 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001610
Doug Zongker32b527d2014-03-04 10:03:02 -08001611
Tao Bao15a146a2018-02-21 16:06:59 -08001612def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001613 """Returns a target-files.zip file for generating secondary payload.
1614
1615 Although the original target-files.zip already contains secondary slot
1616 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1617 ones without _other suffix. Note that we cannot instead modify the names in
1618 META/ab_partitions.txt, because there are no matching partitions on device.
1619
1620 For the partitions that don't have secondary images, the ones for primary
1621 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1622 bootloader images in the inactive slot.
1623
1624 Args:
1625 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001626 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001627
1628 Returns:
1629 The filename of the target-files.zip for generating secondary payload.
1630 """
1631 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1632 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1633
Tao Baodba59ee2018-01-09 13:21:02 -08001634 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
1635 with zipfile.ZipFile(input_file, 'r') as input_zip:
1636 infolist = input_zip.infolist()
1637
1638 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001639 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1640 if info.filename == 'IMAGES/system_other.img':
1641 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1642
1643 # Primary images and friends need to be skipped explicitly.
1644 elif info.filename in ('IMAGES/system.img',
1645 'IMAGES/system.map'):
1646 pass
1647
Tao Bao15a146a2018-02-21 16:06:59 -08001648 # Skip copying the postinstall config if requested.
1649 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1650 pass
1651
Tao Baof7140c02018-01-30 17:09:24 -08001652 elif info.filename.startswith(('META/', 'IMAGES/')):
1653 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1654
Tao Baof7140c02018-01-30 17:09:24 -08001655 common.ZipClose(target_zip)
1656
1657 return target_file
1658
1659
Tao Bao15a146a2018-02-21 16:06:59 -08001660def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1661 """Returns a target-files.zip that's not containing postinstall_config.txt.
1662
1663 This allows brillo_update_payload script to skip writing all the postinstall
1664 hooks in the generated payload. The input target-files.zip file will be
1665 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1666 contain the postinstall_config.txt entry, the input file will be returned.
1667
1668 Args:
1669 input_file: The input target-files.zip filename.
1670
1671 Returns:
1672 The filename of target-files.zip that doesn't contain postinstall config.
1673 """
1674 # We should only make a copy if postinstall_config entry exists.
1675 with zipfile.ZipFile(input_file, 'r') as input_zip:
1676 if POSTINSTALL_CONFIG not in input_zip.namelist():
1677 return input_file
1678
1679 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1680 shutil.copyfile(input_file, target_file)
1681 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1682 return target_file
1683
1684
Tao Baoc098e9e2016-01-07 13:03:56 -08001685def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1686 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001687 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001688 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001689 if not OPTIONS.no_signing:
1690 staging_file = common.MakeTempFile(suffix='.zip')
1691 else:
1692 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001693 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001694 compression=zipfile.ZIP_DEFLATED)
1695
Tao Bao481bab82017-12-21 11:23:09 -08001696 if source_file is not None:
1697 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1698 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1699 else:
1700 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1701 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001702
Tao Bao481bab82017-12-21 11:23:09 -08001703 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001704 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001705
Tao Bao15a146a2018-02-21 16:06:59 -08001706 if OPTIONS.skip_postinstall:
1707 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1708
Tao Bao40b18822018-01-30 18:19:04 -08001709 # Generate payload.
1710 payload = Payload()
1711
1712 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001713 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001714 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001715 else:
1716 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001717 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001718
Tao Bao40b18822018-01-30 18:19:04 -08001719 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001720
Tao Bao40b18822018-01-30 18:19:04 -08001721 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001722 payload_signer = PayloadSigner()
1723 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001724
Tao Bao40b18822018-01-30 18:19:04 -08001725 # Write the payload into output zip.
1726 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001727
Tao Baof7140c02018-01-30 17:09:24 -08001728 # Generate and include the secondary payload that installs secondary images
1729 # (e.g. system_other.img).
1730 if OPTIONS.include_secondary:
1731 # We always include a full payload for the secondary slot, even when
1732 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001733 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1734 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001735 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001736 secondary_payload.Generate(secondary_target_file,
1737 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001738 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001739 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001740
Tianjie Xucfa86222016-03-07 16:31:19 -08001741 # If dm-verity is supported for the device, copy contents of care_map
1742 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001743 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001744 if (target_info.get("verity") == "true" or
1745 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001746 care_map_path = "META/care_map.txt"
1747 namelist = target_zip.namelist()
1748 if care_map_path in namelist:
1749 care_map_data = target_zip.read(care_map_path)
Tao Bao40b18822018-01-30 18:19:04 -08001750 # In order to support streaming, care_map.txt needs to be packed as
1751 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001752 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001753 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001754 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001755 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001756
Tao Baobcd1d162017-08-26 13:10:26 -07001757 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001758 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001759
Tao Bao21803d32017-04-19 10:16:09 -07001760 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001761
Tao Baofe5b69a2018-03-02 09:47:43 -08001762 # We haven't written the metadata entry yet, which will be handled in
1763 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001764 common.ZipClose(output_zip)
1765
Tao Bao85f16982018-03-08 16:28:33 -08001766 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1767 # all the info of the latter. However, system updaters and OTA servers need to
1768 # take time to switch to the new flag. We keep both of the flags for
1769 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001770 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001771 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001772 StreamingPropertyFiles(),
1773 )
1774 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001775
Tao Baoc098e9e2016-01-07 13:03:56 -08001776
Doug Zongkereef39442009-04-02 12:14:19 -07001777def main(argv):
1778
1779 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001780 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001781 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001782 elif o in ("-i", "--incremental_from"):
1783 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001784 elif o == "--full_radio":
1785 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001786 elif o == "--full_bootloader":
1787 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001788 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001789 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001790 elif o == "--downgrade":
1791 OPTIONS.downgrade = True
1792 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001793 elif o == "--override_timestamp":
1794 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001795 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001796 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001797 elif o == "--oem_no_mount":
1798 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001799 elif o in ("-e", "--extra_script"):
1800 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001801 elif o in ("-t", "--worker_threads"):
1802 if a.isdigit():
1803 OPTIONS.worker_threads = int(a)
1804 else:
1805 raise ValueError("Cannot parse value %r for option %r - only "
1806 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001807 elif o in ("-2", "--two_step"):
1808 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001809 elif o == "--include_secondary":
1810 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001811 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001812 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001813 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001814 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001815 elif o == "--block":
1816 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001817 elif o in ("-b", "--binary"):
1818 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001819 elif o == "--stash_threshold":
1820 try:
1821 OPTIONS.stash_threshold = float(a)
1822 except ValueError:
1823 raise ValueError("Cannot parse value %r for option %r - expecting "
1824 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001825 elif o == "--log_diff":
1826 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001827 elif o == "--payload_signer":
1828 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001829 elif o == "--payload_signer_args":
1830 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001831 elif o == "--extracted_input_target_files":
1832 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001833 elif o == "--skip_postinstall":
1834 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001835 else:
1836 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001837 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001838
1839 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001840 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001841 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001842 "package_key=",
1843 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001844 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001845 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001846 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001847 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001848 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001849 "extra_script=",
1850 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001851 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001852 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001853 "no_signing",
1854 "block",
1855 "binary=",
1856 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001857 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001858 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001859 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001860 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001861 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001862 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001863 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001864 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001865 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001866
1867 if len(args) != 2:
1868 common.Usage(__doc__)
1869 sys.exit(1)
1870
Tao Bao5d182562016-02-23 11:38:39 -08001871 if OPTIONS.downgrade:
1872 # Sanity check to enforce a data wipe.
1873 if not OPTIONS.wipe_user_data:
1874 raise ValueError("Cannot downgrade without a data wipe")
1875
1876 # We should only allow downgrading incrementals (as opposed to full).
1877 # Otherwise the device may go back from arbitrary build with this full
1878 # OTA package.
1879 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001880 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001881
Tao Bao3e6161a2017-02-28 11:48:48 -08001882 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1883 "Cannot have --downgrade AND --override_timestamp both"
1884
Tao Bao2db13852018-01-08 22:28:57 -08001885 # Load the build info dicts from the zip directly or the extracted input
1886 # directory. We don't need to unzip the entire target-files zips, because they
1887 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1888 # When loading the info dicts, we don't need to provide the second parameter
1889 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1890 # some properties with their actual paths, such as 'selinux_fc',
1891 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001892 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001893 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001894 else:
Tao Bao2db13852018-01-08 22:28:57 -08001895 with zipfile.ZipFile(args[0], 'r') as input_zip:
1896 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001897
Tao Bao2db13852018-01-08 22:28:57 -08001898 if OPTIONS.verbose:
1899 print("--- target info ---")
1900 common.DumpInfoDict(OPTIONS.info_dict)
1901
1902 # Load the source build dict if applicable.
1903 if OPTIONS.incremental_source is not None:
1904 OPTIONS.target_info_dict = OPTIONS.info_dict
1905 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1906 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1907
1908 if OPTIONS.verbose:
1909 print("--- source info ---")
1910 common.DumpInfoDict(OPTIONS.source_info_dict)
1911
1912 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001913 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1914
Tao Baoc098e9e2016-01-07 13:03:56 -08001915 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1916
Christian Oderf63e2cd2017-05-01 22:30:15 +02001917 # Use the default key to sign the package if not specified with package_key.
1918 # package_keys are needed on ab_updates, so always define them if an
1919 # ab_update is getting created.
1920 if not OPTIONS.no_signing or ab_update:
1921 if OPTIONS.package_key is None:
1922 OPTIONS.package_key = OPTIONS.info_dict.get(
1923 "default_system_dev_certificate",
1924 "build/target/product/security/testkey")
1925 # Get signing keys
1926 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1927
Tao Baoc098e9e2016-01-07 13:03:56 -08001928 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001929 WriteABOTAPackageWithBrilloScript(
1930 target_file=args[0],
1931 output_file=args[1],
1932 source_file=OPTIONS.incremental_source)
1933
Tao Bao89fbb0f2017-01-10 10:47:58 -08001934 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001935 return
1936
Tao Bao2db13852018-01-08 22:28:57 -08001937 # Sanity check the loaded info dicts first.
1938 if OPTIONS.info_dict.get("no_recovery") == "true":
1939 raise common.ExternalError(
1940 "--- target build has specified no recovery ---")
1941
1942 # Non-A/B OTAs rely on /cache partition to store temporary files.
1943 cache_size = OPTIONS.info_dict.get("cache_size")
1944 if cache_size is None:
1945 print("--- can't determine the cache partition size ---")
1946 OPTIONS.cache_size = cache_size
1947
Doug Zongker1c390a22009-05-14 19:06:36 -07001948 if OPTIONS.extra_script is not None:
1949 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1950
Dan Willemsencea5cd22017-03-21 14:44:27 -07001951 if OPTIONS.extracted_input is not None:
1952 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001953 else:
1954 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001955 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001956 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001957
Tao Bao2db13852018-01-08 22:28:57 -08001958 # If the caller explicitly specified the device-specific extensions path via
1959 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1960 # is present in the target target_files. Otherwise, take the path of the file
1961 # from 'tool_extensions' in the info dict and look for that in the local
1962 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001963 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001964 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1965 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001966 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001967 OPTIONS.device_specific = from_input
1968 else:
Tao Bao2db13852018-01-08 22:28:57 -08001969 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001970
Doug Zongker37974732010-09-16 17:44:38 -07001971 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001972 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001973
Tao Bao767e3ac2015-11-10 12:19:19 -08001974 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001975 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08001976 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08001977 WriteFullOTAPackage(
1978 input_zip,
1979 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08001980
Tao Bao32b80dc2018-01-08 22:50:47 -08001981 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001982 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001983 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001984 OPTIONS.source_tmp = common.UnzipTemp(
1985 OPTIONS.incremental_source, UNZIP_PATTERN)
1986 with zipfile.ZipFile(args[0], 'r') as input_zip, \
1987 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08001988 WriteBlockIncrementalOTAPackage(
1989 input_zip,
1990 source_zip,
1991 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08001992
1993 if OPTIONS.log_diff:
1994 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001995 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001996 target_files_diff.recursiveDiff(
1997 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001998
Tao Bao89fbb0f2017-01-10 10:47:58 -08001999 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002000
2001
2002if __name__ == '__main__':
2003 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002004 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002005 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002006 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002007 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002008 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002009 finally:
2010 common.Cleanup()