blob: 816b8f5a29dd274bc72b4875db11bd872730fc0b [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 Bao481bab82017-12-21 11:23:09 -0800162import subprocess
163import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700164import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700165import zipfile
166
167import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700168import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -0700169
Tao Bao481bab82017-12-21 11:23:09 -0800170if sys.hexversion < 0x02070000:
171 print("Python 2.7 or newer is required.", file=sys.stderr)
172 sys.exit(1)
173
174
Doug Zongkereef39442009-04-02 12:14:19 -0700175OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700176OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700177OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700178OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700179OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700180OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800181OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800182OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700183OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700184OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
185if OPTIONS.worker_threads == 0:
186 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800187OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800188OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900189OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800190OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800191OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700192OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800193OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700194OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700195OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700196# Stash size cannot exceed cache_size * threshold.
197OPTIONS.cache_size = None
198OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800199OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700200OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700201OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700202OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200203OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800204OPTIONS.skip_postinstall = False
205
Tao Bao8dcf7382015-05-21 14:09:49 -0700206
Tao Bao2dd1c482017-02-03 16:49:39 -0800207METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800208POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800209UNZIP_PATTERN = ['IMAGES/*', 'META/*']
210
Tao Bao2dd1c482017-02-03 16:49:39 -0800211
Tao Bao481bab82017-12-21 11:23:09 -0800212class BuildInfo(object):
213 """A class that holds the information for a given build.
214
215 This class wraps up the property querying for a given source or target build.
216 It abstracts away the logic of handling OEM-specific properties, and caches
217 the commonly used properties such as fingerprint.
218
219 There are two types of info dicts: a) build-time info dict, which is generated
220 at build time (i.e. included in a target_files zip); b) OEM info dict that is
221 specified at package generation time (via command line argument
222 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
223 having "oem_fingerprint_properties" in build-time info dict), all the queries
224 would be answered based on build-time info dict only. Otherwise if using
225 OEM-specific properties, some of them will be calculated from two info dicts.
226
227 Users can query properties similarly as using a dict() (e.g. info['fstab']),
228 or to query build properties via GetBuildProp() or GetVendorBuildProp().
229
230 Attributes:
231 info_dict: The build-time info dict.
232 is_ab: Whether it's a build that uses A/B OTA.
233 oem_dicts: A list of OEM dicts.
234 oem_props: A list of OEM properties that should be read from OEM dicts; None
235 if the build doesn't use any OEM-specific property.
236 fingerprint: The fingerprint of the build, which would be calculated based
237 on OEM properties if applicable.
238 device: The device name, which could come from OEM dicts if applicable.
239 """
240
241 def __init__(self, info_dict, oem_dicts):
242 """Initializes a BuildInfo instance with the given dicts.
243
244 Arguments:
245 info_dict: The build-time info dict.
246 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
247 that it always uses the first dict to calculate the fingerprint or the
248 device name. The rest would be used for asserting OEM properties only
249 (e.g. one package can be installed on one of these devices).
250 """
251 self.info_dict = info_dict
252 self.oem_dicts = oem_dicts
253
254 self._is_ab = info_dict.get("ab_update") == "true"
255 self._oem_props = info_dict.get("oem_fingerprint_properties")
256
257 if self._oem_props:
258 assert oem_dicts, "OEM source required for this build"
259
260 # These two should be computed only after setting self._oem_props.
261 self._device = self.GetOemProperty("ro.product.device")
262 self._fingerprint = self.CalculateFingerprint()
263
264 @property
265 def is_ab(self):
266 return self._is_ab
267
268 @property
269 def device(self):
270 return self._device
271
272 @property
273 def fingerprint(self):
274 return self._fingerprint
275
276 @property
277 def oem_props(self):
278 return self._oem_props
279
280 def __getitem__(self, key):
281 return self.info_dict[key]
282
283 def get(self, key, default=None):
284 return self.info_dict.get(key, default)
285
286 def GetBuildProp(self, prop):
287 """Returns the inquired build property."""
288 try:
289 return self.info_dict.get("build.prop", {})[prop]
290 except KeyError:
291 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
292
293 def GetVendorBuildProp(self, prop):
294 """Returns the inquired vendor build property."""
295 try:
296 return self.info_dict.get("vendor.build.prop", {})[prop]
297 except KeyError:
298 raise common.ExternalError(
299 "couldn't find %s in vendor.build.prop" % (prop,))
300
301 def GetOemProperty(self, key):
302 if self.oem_props is not None and key in self.oem_props:
303 return self.oem_dicts[0][key]
304 return self.GetBuildProp(key)
305
306 def CalculateFingerprint(self):
307 if self.oem_props is None:
308 return self.GetBuildProp("ro.build.fingerprint")
309 return "%s/%s/%s:%s" % (
310 self.GetOemProperty("ro.product.brand"),
311 self.GetOemProperty("ro.product.name"),
312 self.GetOemProperty("ro.product.device"),
313 self.GetBuildProp("ro.build.thumbprint"))
314
315 def WriteMountOemScript(self, script):
316 assert self.oem_props is not None
317 recovery_mount_options = self.info_dict.get("recovery_mount_options")
318 script.Mount("/oem", recovery_mount_options)
319
320 def WriteDeviceAssertions(self, script, oem_no_mount):
321 # Read the property directly if not using OEM properties.
322 if not self.oem_props:
323 script.AssertDevice(self.device)
324 return
325
326 # Otherwise assert OEM properties.
327 if not self.oem_dicts:
328 raise common.ExternalError(
329 "No OEM file provided to answer expected assertions")
330
331 for prop in self.oem_props.split():
332 values = []
333 for oem_dict in self.oem_dicts:
334 if prop in oem_dict:
335 values.append(oem_dict[prop])
336 if not values:
337 raise common.ExternalError(
338 "The OEM file is missing the property %s" % (prop,))
339 script.AssertOemProperty(prop, values, oem_no_mount)
340
341
Tao Baofabe0832018-01-17 15:52:28 -0800342class PayloadSigner(object):
343 """A class that wraps the payload signing works.
344
345 When generating a Payload, hashes of the payload and metadata files will be
346 signed with the device key, either by calling an external payload signer or
347 by calling openssl with the package key. This class provides a unified
348 interface, so that callers can just call PayloadSigner.Sign().
349
350 If an external payload signer has been specified (OPTIONS.payload_signer), it
351 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
352 that the signing key should be provided as part of the payload_signer_args.
353 Otherwise without an external signer, it uses the package key
354 (OPTIONS.package_key) and calls openssl for the signing works.
355 """
356
357 def __init__(self):
358 if OPTIONS.payload_signer is None:
359 # Prepare the payload signing key.
360 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
361 pw = OPTIONS.key_passwords[OPTIONS.package_key]
362
363 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
364 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
365 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
366 cmd.extend(["-out", signing_key])
367
368 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
369 stderr=subprocess.STDOUT)
370 stdoutdata, _ = get_signing_key.communicate()
371 assert get_signing_key.returncode == 0, \
372 "Failed to get signing key: {}".format(stdoutdata)
373
374 self.signer = "openssl"
375 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
376 "-pkeyopt", "digest:sha256"]
377 else:
378 self.signer = OPTIONS.payload_signer
379 self.signer_args = OPTIONS.payload_signer_args
380
381 def Sign(self, in_file):
382 """Signs the given input file. Returns the output filename."""
383 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
384 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
385 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
386 stdoutdata, _ = signing.communicate()
387 assert signing.returncode == 0, \
388 "Failed to sign the input file: {}".format(stdoutdata)
389 return out_file
390
391
Tao Bao40b18822018-01-30 18:19:04 -0800392class Payload(object):
393 """Manages the creation and the signing of an A/B OTA Payload."""
394
395 PAYLOAD_BIN = 'payload.bin'
396 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800397 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
398 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800399
Tao Bao667ff572018-02-10 00:02:40 -0800400 def __init__(self, secondary=False):
401 """Initializes a Payload instance.
402
403 Args:
404 secondary: Whether it's generating a secondary payload (default: False).
405 """
Tao Bao40b18822018-01-30 18:19:04 -0800406 # The place where the output from the subprocess should go.
407 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
408 self.payload_file = None
409 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800410 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800411
412 def Generate(self, target_file, source_file=None, additional_args=None):
413 """Generates a payload from the given target-files zip(s).
414
415 Args:
416 target_file: The filename of the target build target-files zip.
417 source_file: The filename of the source build target-files zip; or None if
418 generating a full OTA.
419 additional_args: A list of additional args that should be passed to
420 brillo_update_payload script; or None.
421 """
422 if additional_args is None:
423 additional_args = []
424
425 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
426 cmd = ["brillo_update_payload", "generate",
427 "--payload", payload_file,
428 "--target_image", target_file]
429 if source_file is not None:
430 cmd.extend(["--source_image", source_file])
431 cmd.extend(additional_args)
432 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
433 stdoutdata, _ = p.communicate()
434 assert p.returncode == 0, \
435 "brillo_update_payload generate failed: {}".format(stdoutdata)
436
437 self.payload_file = payload_file
438 self.payload_properties = None
439
440 def Sign(self, payload_signer):
441 """Generates and signs the hashes of the payload and metadata.
442
443 Args:
444 payload_signer: A PayloadSigner() instance that serves the signing work.
445
446 Raises:
447 AssertionError: On any failure when calling brillo_update_payload script.
448 """
449 assert isinstance(payload_signer, PayloadSigner)
450
451 # 1. Generate hashes of the payload and metadata files.
452 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
453 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
454 cmd = ["brillo_update_payload", "hash",
455 "--unsigned_payload", self.payload_file,
456 "--signature_size", "256",
457 "--metadata_hash_file", metadata_sig_file,
458 "--payload_hash_file", payload_sig_file]
459 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
460 p1.communicate()
461 assert p1.returncode == 0, "brillo_update_payload hash failed"
462
463 # 2. Sign the hashes.
464 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
465 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
466
467 # 3. Insert the signatures back into the payload file.
468 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
469 suffix=".bin")
470 cmd = ["brillo_update_payload", "sign",
471 "--unsigned_payload", self.payload_file,
472 "--payload", signed_payload_file,
473 "--signature_size", "256",
474 "--metadata_signature_file", signed_metadata_sig_file,
475 "--payload_signature_file", signed_payload_sig_file]
476 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
477 p1.communicate()
478 assert p1.returncode == 0, "brillo_update_payload sign failed"
479
480 # 4. Dump the signed payload properties.
481 properties_file = common.MakeTempFile(prefix="payload-properties-",
482 suffix=".txt")
483 cmd = ["brillo_update_payload", "properties",
484 "--payload", signed_payload_file,
485 "--properties_file", properties_file]
486 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
487 p1.communicate()
488 assert p1.returncode == 0, "brillo_update_payload properties failed"
489
Tao Bao667ff572018-02-10 00:02:40 -0800490 if self.secondary:
491 with open(properties_file, "a") as f:
492 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
493
Tao Bao40b18822018-01-30 18:19:04 -0800494 if OPTIONS.wipe_user_data:
495 with open(properties_file, "a") as f:
496 f.write("POWERWASH=1\n")
497
498 self.payload_file = signed_payload_file
499 self.payload_properties = properties_file
500
Tao Bao667ff572018-02-10 00:02:40 -0800501 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800502 """Writes the payload to the given zip.
503
504 Args:
505 output_zip: The output ZipFile instance.
506 """
507 assert self.payload_file is not None
508 assert self.payload_properties is not None
509
Tao Bao667ff572018-02-10 00:02:40 -0800510 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800511 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
512 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
513 else:
514 payload_arcname = Payload.PAYLOAD_BIN
515 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
516
Tao Bao40b18822018-01-30 18:19:04 -0800517 # Add the signed payload file and properties into the zip. In order to
518 # support streaming, we pack them as ZIP_STORED. So these entries can be
519 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800520 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800521 compress_type=zipfile.ZIP_STORED)
522 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800523 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800524 compress_type=zipfile.ZIP_STORED)
525
526
Doug Zongkereef39442009-04-02 12:14:19 -0700527def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200528 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700529
Doug Zongker951495f2009-08-14 12:44:19 -0700530 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
531 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700532
533
Tao Bao481bab82017-12-21 11:23:09 -0800534def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800535 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800536 if not oem_source:
537 return None
538
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800539 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800540 for oem_file in oem_source:
541 with open(oem_file) as fp:
542 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800543 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700544
Doug Zongkereef39442009-04-02 12:14:19 -0700545
Tao Baod42e97e2016-11-30 12:11:57 -0800546def _WriteRecoveryImageToBoot(script, output_zip):
547 """Find and write recovery image to /boot in two-step OTA.
548
549 In two-step OTAs, we write recovery image to /boot as the first step so that
550 we can reboot to there and install a new recovery image to /recovery.
551 A special "recovery-two-step.img" will be preferred, which encodes the correct
552 path of "/boot". Otherwise the device may show "device is corrupt" message
553 when booting into /boot.
554
555 Fall back to using the regular recovery.img if the two-step recovery image
556 doesn't exist. Note that rebuilding the special image at this point may be
557 infeasible, because we don't have the desired boot signer and keys when
558 calling ota_from_target_files.py.
559 """
560
561 recovery_two_step_img_name = "recovery-two-step.img"
562 recovery_two_step_img_path = os.path.join(
563 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
564 if os.path.exists(recovery_two_step_img_path):
565 recovery_two_step_img = common.GetBootableImage(
566 recovery_two_step_img_name, recovery_two_step_img_name,
567 OPTIONS.input_tmp, "RECOVERY")
568 common.ZipWriteStr(
569 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800570 print("two-step package: using %s in stage 1/3" % (
571 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800572 script.WriteRawImage("/boot", recovery_two_step_img_name)
573 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800574 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800575 # The "recovery.img" entry has been written into package earlier.
576 script.WriteRawImage("/boot", "recovery.img")
577
578
Doug Zongkerc9253822014-02-04 12:17:58 -0800579def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700580 namelist = [name for name in target_files_zip.namelist()]
581 return ("SYSTEM/recovery-from-boot.p" in namelist or
582 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700583
Tao Bao457cbf62017-03-06 09:56:01 -0800584
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700585def HasVendorPartition(target_files_zip):
586 try:
587 target_files_zip.getinfo("VENDOR/")
588 return True
589 except KeyError:
590 return False
591
Tao Bao457cbf62017-03-06 09:56:01 -0800592
Tao Bao481bab82017-12-21 11:23:09 -0800593def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700594 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800595 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700596
597
Tao Bao481bab82017-12-21 11:23:09 -0800598def WriteFingerprintAssertion(script, target_info, source_info):
599 source_oem_props = source_info.oem_props
600 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700601
Tao Bao481bab82017-12-21 11:23:09 -0800602 if source_oem_props is None and target_oem_props is None:
603 script.AssertSomeFingerprint(
604 source_info.fingerprint, target_info.fingerprint)
605 elif source_oem_props is not None and target_oem_props is not None:
606 script.AssertSomeThumbprint(
607 target_info.GetBuildProp("ro.build.thumbprint"),
608 source_info.GetBuildProp("ro.build.thumbprint"))
609 elif source_oem_props is None and target_oem_props is not None:
610 script.AssertFingerprintOrThumbprint(
611 source_info.fingerprint,
612 target_info.GetBuildProp("ro.build.thumbprint"))
613 else:
614 script.AssertFingerprintOrThumbprint(
615 target_info.fingerprint,
616 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700617
Doug Zongkerfc44a512014-08-26 13:10:25 -0700618
Tao Bao481bab82017-12-21 11:23:09 -0800619def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
620 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700621 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700622
623 Metadata used for on-device compatibility verification is retrieved from
624 target_zip then added to compatibility.zip which is added to the output_zip
625 archive.
626
Tao Baobcd1d162017-08-26 13:10:26 -0700627 Compatibility archive should only be included for devices that have enabled
628 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700629
630 Args:
631 target_zip: Zip file containing the source files to be included for OTA.
632 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800633 target_info: The BuildInfo instance that holds the target build info.
634 source_info: The BuildInfo instance that holds the source build info, if
635 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700636 """
637
Tao Baobcd1d162017-08-26 13:10:26 -0700638 def AddCompatibilityArchive(system_updated, vendor_updated):
639 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700640
Tao Baobcd1d162017-08-26 13:10:26 -0700641 Args:
642 system_updated: If True, the system image will be updated and therefore
643 its metadata should be included.
644 vendor_updated: If True, the vendor image will be updated and therefore
645 its metadata should be included.
646 """
647 # Determine what metadata we need. Files are names relative to META/.
648 compatibility_files = []
649 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
650 system_metadata = ("system_manifest.xml", "system_matrix.xml")
651 if vendor_updated:
652 compatibility_files += vendor_metadata
653 if system_updated:
654 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700655
Tao Baobcd1d162017-08-26 13:10:26 -0700656 # Create new archive.
657 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800658 compatibility_archive_zip = zipfile.ZipFile(
659 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700660
Tao Baobcd1d162017-08-26 13:10:26 -0700661 # Add metadata.
662 for file_name in compatibility_files:
663 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700664
Tao Baobcd1d162017-08-26 13:10:26 -0700665 if target_file_name in target_zip.namelist():
666 data = target_zip.read(target_file_name)
667 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700668
Tao Baobcd1d162017-08-26 13:10:26 -0700669 # Ensure files are written before we copy into output_zip.
670 compatibility_archive_zip.close()
671
672 # Only add the archive if we have any compatibility info.
673 if compatibility_archive_zip.namelist():
674 common.ZipWrite(output_zip, compatibility_archive.name,
675 arcname="compatibility.zip",
676 compress_type=zipfile.ZIP_STORED)
677
678 # Will only proceed if the target has enabled the Treble support (as well as
679 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800680 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700681 return
682
683 # We don't support OEM thumbprint in Treble world (which calculates
684 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800685 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700686
687 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800688 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700689 AddCompatibilityArchive(True, True)
690 return
691
Tao Bao481bab82017-12-21 11:23:09 -0800692 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700693
Tao Bao481bab82017-12-21 11:23:09 -0800694 source_fp = source_info.fingerprint
695 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700696 system_updated = source_fp != target_fp
697
Tao Bao481bab82017-12-21 11:23:09 -0800698 source_fp_vendor = source_info.GetVendorBuildProp(
699 "ro.vendor.build.fingerprint")
700 target_fp_vendor = target_info.GetVendorBuildProp(
701 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700702 vendor_updated = source_fp_vendor != target_fp_vendor
703
704 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700705
706
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700707def WriteFullOTAPackage(input_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800708 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700709
Tao Bao481bab82017-12-21 11:23:09 -0800710 # We don't know what version it will be installed on top of. We expect the API
711 # just won't change very often. Similarly for fstab, it might have changed in
712 # the target build.
713 target_api_version = target_info["recovery_api_version"]
714 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700715
Tao Bao481bab82017-12-21 11:23:09 -0800716 if target_info.oem_props and not OPTIONS.oem_no_mount:
717 target_info.WriteMountOemScript(script)
718
Tao Baodf3a48b2018-01-10 16:30:43 -0800719 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700720
Doug Zongker05d3dea2009-06-22 11:32:31 -0700721 device_specific = common.DeviceSpecificParams(
722 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800723 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700724 output_zip=output_zip,
725 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700726 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700727 metadata=metadata,
728 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700729
Tao Bao457cbf62017-03-06 09:56:01 -0800730 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800731
Tao Bao481bab82017-12-21 11:23:09 -0800732 # Assertions (e.g. downgrade check, device properties check).
733 ts = target_info.GetBuildProp("ro.build.date.utc")
734 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700735 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700736
Tao Bao481bab82017-12-21 11:23:09 -0800737 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700738 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800739
740 # Two-step package strategy (in chronological order, which is *not*
741 # the order in which the generated script has things):
742 #
743 # if stage is not "2/3" or "3/3":
744 # write recovery image to boot partition
745 # set stage to "2/3"
746 # reboot to boot partition and restart recovery
747 # else if stage is "2/3":
748 # write recovery image to recovery partition
749 # set stage to "3/3"
750 # reboot to recovery partition and restart recovery
751 # else:
752 # (stage must be "3/3")
753 # set stage to ""
754 # do normal full package installation:
755 # wipe and install system, boot image, etc.
756 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700757 # complete script normally
758 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800759
760 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
761 OPTIONS.input_tmp, "RECOVERY")
762 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800763 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800764 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800765 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800766 assert fs.fs_type.upper() == "EMMC", \
767 "two-step packages only supported on devices with EMMC /misc partitions"
768 bcb_dev = {"bcb_dev": fs.device}
769 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
770 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700771if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800772""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800773
774 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
775 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800776 script.WriteRawImage("/recovery", "recovery.img")
777 script.AppendExtra("""
778set_stage("%(bcb_dev)s", "3/3");
779reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700780else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800781""" % bcb_dev)
782
Tao Baod42e97e2016-11-30 12:11:57 -0800783 # Stage 3/3: Make changes.
784 script.Comment("Stage 3/3")
785
Tao Bao6c55a8a2015-04-08 15:30:27 -0700786 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800787 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700788
Doug Zongkere5ff5902012-01-17 10:55:37 -0800789 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700790
Doug Zongker01ce19c2014-02-04 13:48:15 -0800791 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700792
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700793 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800794 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700795 if HasVendorPartition(input_zip):
796 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700797
Doug Zongker4b9596f2014-06-09 14:15:45 -0700798 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800799
Tao Baoe709b092018-02-07 12:40:00 -0800800 # See the notes in WriteBlockIncrementalOTAPackage().
801 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
802
Tao Bao457cbf62017-03-06 09:56:01 -0800803 # Full OTA is done as an "incremental" against an empty source image. This
804 # has the effect of writing new data from the package to the entire
805 # partition, but lets us reuse the updater code that writes incrementals to
806 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800807 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
808 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800809 system_tgt.ResetFileMap()
810 system_diff = common.BlockDifference("system", system_tgt, src=None)
811 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700812
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700813 boot_img = common.GetBootableImage(
814 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800815
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700816 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700817 script.ShowProgress(0.1, 0)
818
Tao Baoe709b092018-02-07 12:40:00 -0800819 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
820 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800821 vendor_tgt.ResetFileMap()
822 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
823 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700824
Tao Bao481bab82017-12-21 11:23:09 -0800825 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700826
Tao Bao481bab82017-12-21 11:23:09 -0800827 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700828 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700829
Doug Zongker01ce19c2014-02-04 13:48:15 -0800830 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700831 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700832
Doug Zongker01ce19c2014-02-04 13:48:15 -0800833 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700834 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700835
Doug Zongker1c390a22009-05-14 19:06:36 -0700836 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700837 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700838
Doug Zongker14833602010-02-02 13:12:04 -0800839 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800840
Doug Zongker922206e2014-03-04 13:16:24 -0800841 if OPTIONS.wipe_user_data:
842 script.ShowProgress(0.1, 10)
843 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700844
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800845 if OPTIONS.two_step:
846 script.AppendExtra("""
847set_stage("%(bcb_dev)s", "");
848""" % bcb_dev)
849 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800850
851 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
852 script.Comment("Stage 1/3")
853 _WriteRecoveryImageToBoot(script, output_zip)
854
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800855 script.AppendExtra("""
856set_stage("%(bcb_dev)s", "2/3");
857reboot_now("%(bcb_dev)s", "");
858endif;
859endif;
860""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800861
Tao Bao5d182562016-02-23 11:38:39 -0800862 script.SetProgress(1)
863 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800864 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700865 WriteMetadata(metadata, output_zip)
866
Doug Zongkerfc44a512014-08-26 13:10:25 -0700867
Doug Zongker2ea21062010-04-28 16:05:21 -0700868def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800869 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
870 common.ZipWriteStr(output_zip, METADATA_NAME, value,
871 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700872
Doug Zongkerfc44a512014-08-26 13:10:25 -0700873
Tao Bao481bab82017-12-21 11:23:09 -0800874def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800875 # Only incremental OTAs are allowed to reach here.
876 assert OPTIONS.incremental_source is not None
877
Tao Bao481bab82017-12-21 11:23:09 -0800878 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
879 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800880 is_downgrade = long(post_timestamp) < long(pre_timestamp)
881
882 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800883 if not is_downgrade:
884 raise RuntimeError("--downgrade specified but no downgrade detected: "
885 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800886 metadata["ota-downgrade"] = "yes"
887 elif OPTIONS.timestamp:
888 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800889 raise RuntimeError("--override_timestamp specified but no timestamp hack "
890 "needed: pre: %s, post: %s" % (pre_timestamp,
891 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800892 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800893 else:
894 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800895 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800896 "pre: %s, post: %s. Need to specify "
897 "--override_timestamp OR --downgrade to allow "
898 "building the incremental." % (pre_timestamp,
899 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800900 metadata["post-timestamp"] = post_timestamp
901
902
Tao Baodf3a48b2018-01-10 16:30:43 -0800903def GetPackageMetadata(target_info, source_info=None):
904 """Generates and returns the metadata dict.
905
906 It generates a dict() that contains the info to be written into an OTA
907 package (META-INF/com/android/metadata). It also handles the detection of
908 downgrade / timestamp override / data wipe based on the global options.
909
910 Args:
911 target_info: The BuildInfo instance that holds the target build info.
912 source_info: The BuildInfo instance that holds the source build info, or
913 None if generating full OTA.
914
915 Returns:
916 A dict to be written into package metadata entry.
917 """
918 assert isinstance(target_info, BuildInfo)
919 assert source_info is None or isinstance(source_info, BuildInfo)
920
921 metadata = {
922 'post-build' : target_info.fingerprint,
923 'post-build-incremental' : target_info.GetBuildProp(
924 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800925 'post-sdk-level' : target_info.GetBuildProp(
926 'ro.build.version.sdk'),
927 'post-security-patch-level' : target_info.GetBuildProp(
928 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800929 }
930
931 if target_info.is_ab:
932 metadata['ota-type'] = 'AB'
933 metadata['ota-required-cache'] = '0'
934 else:
935 metadata['ota-type'] = 'BLOCK'
936
937 if OPTIONS.wipe_user_data:
938 metadata['ota-wipe'] = 'yes'
939
940 is_incremental = source_info is not None
941 if is_incremental:
942 metadata['pre-build'] = source_info.fingerprint
943 metadata['pre-build-incremental'] = source_info.GetBuildProp(
944 'ro.build.version.incremental')
945 metadata['pre-device'] = source_info.device
946 else:
947 metadata['pre-device'] = target_info.device
948
949 # Detect downgrades, or fill in the post-timestamp.
950 if is_incremental:
951 HandleDowngradeMetadata(metadata, target_info, source_info)
952 else:
953 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
954
955 return metadata
956
957
Tao Baod3fc38a2018-03-08 16:09:01 -0800958class PropertyFiles(object):
959 """A class that computes the property-files string for an OTA package.
960
961 A property-files string is a comma-separated string that contains the
962 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
963 can be fetched directly with the package URL along with the offset/size info.
964 These strings can be used for streaming A/B OTAs, or allowing an updater to
965 download package metadata entry directly, without paying the cost of
966 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800967
Tao Baocc8e2662018-03-01 19:30:00 -0800968 Computing the final property-files string requires two passes. Because doing
969 the whole package signing (with signapk.jar) will possibly reorder the ZIP
970 entries, which may in turn invalidate earlier computed ZIP entry offset/size
971 values.
972
973 This class provides functions to be called for each pass. The general flow is
974 as follows.
975
Tao Baod3fc38a2018-03-08 16:09:01 -0800976 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800977 # The first pass, which writes placeholders before doing initial signing.
978 property_files.Compute()
979 SignOutput()
980
981 # The second pass, by replacing the placeholders with actual data.
982 property_files.Finalize()
983 SignOutput()
984
985 And the caller can additionally verify the final result.
986
987 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -0800988 """
989
Tao Baocc8e2662018-03-01 19:30:00 -0800990 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -0800991 self.name = None
992 self.required = ()
993 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -0800994
Tao Baocc8e2662018-03-01 19:30:00 -0800995 def Compute(self, input_zip):
996 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -0800997
Tao Baocc8e2662018-03-01 19:30:00 -0800998 We reserve extra space for the offset and size of the metadata entry itself,
999 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001000
Tao Baocc8e2662018-03-01 19:30:00 -08001001 Args:
1002 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001003
Tao Baocc8e2662018-03-01 19:30:00 -08001004 Returns:
1005 A string with placeholders for the metadata offset/size info, e.g.
1006 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1007 """
1008 return self._GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001009
Tao Baocc8e2662018-03-01 19:30:00 -08001010 def Finalize(self, input_zip, reserved_length):
1011 """Finalizes a property-files string with actual METADATA offset/size info.
1012
1013 The input ZIP file has been signed, with the ZIP entries in the desired
1014 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1015 the ZIP entry offsets and construct the property-files string with actual
1016 data. Note that during this process, we must pad the property-files string
1017 to the reserved length, so that the METADATA entry size remains the same.
1018 Otherwise the entries' offsets and sizes may change again.
1019
1020 Args:
1021 input_zip: The input ZIP file.
1022 reserved_length: The reserved length of the property-files string during
1023 the call to Compute(). The final string must be no more than this
1024 size.
1025
1026 Returns:
1027 A property-files string including the metadata offset/size info, e.g.
1028 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1029
1030 Raises:
1031 AssertionError: If the reserved length is insufficient to hold the final
1032 string.
1033 """
1034 result = self._GetPropertyFilesString(input_zip, reserve_space=False)
1035 assert len(result) <= reserved_length, \
1036 'Insufficient reserved space: reserved={}, actual={}'.format(
1037 reserved_length, len(result))
1038 result += ' ' * (reserved_length - len(result))
1039 return result
1040
1041 def Verify(self, input_zip, expected):
1042 """Verifies the input ZIP file contains the expected property-files string.
1043
1044 Args:
1045 input_zip: The input ZIP file.
1046 expected: The property-files string that's computed from Finalize().
1047
1048 Raises:
1049 AssertionError: On finding a mismatch.
1050 """
1051 actual = self._GetPropertyFilesString(input_zip)
1052 assert actual == expected, \
1053 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1054
1055 def _GetPropertyFilesString(self, zip_file, reserve_space=False):
1056 """Constructs the property-files string per request."""
1057
1058 def ComputeEntryOffsetSize(name):
1059 """Computes the zip entry offset and size."""
1060 info = zip_file.getinfo(name)
1061 offset = info.header_offset + len(info.FileHeader())
1062 size = info.file_size
1063 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1064
1065 tokens = []
1066 for entry in self.required:
1067 tokens.append(ComputeEntryOffsetSize(entry))
1068 for entry in self.optional:
1069 if entry in zip_file.namelist():
1070 tokens.append(ComputeEntryOffsetSize(entry))
1071
1072 # 'META-INF/com/android/metadata' is required. We don't know its actual
1073 # offset and length (as well as the values for other entries). So we reserve
1074 # 10-byte as a placeholder, which is to cover the space for metadata entry
1075 # ('xx:xxx', since it's ZIP_STORED which should appear at the beginning of
1076 # the zip), as well as the possible value changes in other entries.
1077 if reserve_space:
1078 tokens.append('metadata:' + ' ' * 10)
1079 else:
1080 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1081
1082 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001083
1084
Tao Baod3fc38a2018-03-08 16:09:01 -08001085class StreamingPropertyFiles(PropertyFiles):
1086 """A subclass for computing the property-files for streaming A/B OTAs."""
1087
1088 def __init__(self):
1089 super(StreamingPropertyFiles, self).__init__()
1090 self.name = 'ota-streaming-property-files'
1091 self.required = (
1092 # payload.bin and payload_properties.txt must exist.
1093 'payload.bin',
1094 'payload_properties.txt',
1095 )
1096 self.optional = (
1097 # care_map.txt is available only if dm-verity is enabled.
1098 'care_map.txt',
1099 # compatibility.zip is available only if target supports Treble.
1100 'compatibility.zip',
1101 )
1102
1103
1104def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001105 """Finalizes the metadata and signs an A/B OTA package.
1106
1107 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1108 that contains the offsets and sizes for the ZIP entries. An example
1109 property-files string is as follows.
1110
1111 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1112
1113 OTA server can pass down this string, in addition to the package URL, to the
1114 system update client. System update client can then fetch individual ZIP
1115 entries (ZIP_STORED) directly at the given offset of the URL.
1116
1117 Args:
1118 metadata: The metadata dict for the package.
1119 input_file: The input ZIP filename that doesn't contain the package METADATA
1120 entry yet.
1121 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001122 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001123 """
1124 output_zip = zipfile.ZipFile(
1125 input_file, 'a', compression=zipfile.ZIP_DEFLATED)
1126
1127 # Write the current metadata entry with placeholders.
Tao Baod3fc38a2018-03-08 16:09:01 -08001128 for property_files in needed_property_files:
1129 metadata[property_files.name] = property_files.Compute(output_zip)
Tao Baofe5b69a2018-03-02 09:47:43 -08001130 WriteMetadata(metadata, output_zip)
1131 common.ZipClose(output_zip)
1132
1133 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1134 # ZIP entries, as well as padding the entry headers. We do a preliminary
1135 # signing (with an incomplete metadata entry) to allow that to happen. Then
1136 # compute the ZIP entry offsets, write back the final metadata and do the
1137 # final signing.
1138 prelim_signing = common.MakeTempFile(suffix='.zip')
1139 SignOutput(input_file, prelim_signing)
1140
1141 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Baocc8e2662018-03-01 19:30:00 -08001142 with zipfile.ZipFile(prelim_signing, 'r') as prelim_signing_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001143 for property_files in needed_property_files:
1144 metadata[property_files.name] = property_files.Finalize(
1145 prelim_signing_zip, len(metadata[property_files.name]))
Tao Baofe5b69a2018-03-02 09:47:43 -08001146
1147 # Replace the METADATA entry.
1148 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod3fc38a2018-03-08 16:09:01 -08001149 output_zip = zipfile.ZipFile(
1150 prelim_signing, 'a', compression=zipfile.ZIP_DEFLATED)
Tao Baofe5b69a2018-03-02 09:47:43 -08001151 WriteMetadata(metadata, output_zip)
1152 common.ZipClose(output_zip)
1153
1154 # Re-sign the package after updating the metadata entry.
1155 SignOutput(prelim_signing, output_file)
1156
1157 # Reopen the final signed zip to double check the streaming metadata.
Tao Baocc8e2662018-03-01 19:30:00 -08001158 with zipfile.ZipFile(output_file, 'r') as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001159 for property_files in needed_property_files:
1160 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001161
1162
Geremy Condra36bd3652014-02-06 19:45:10 -08001163def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -08001164 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1165 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001166
Tao Bao481bab82017-12-21 11:23:09 -08001167 target_api_version = target_info["recovery_api_version"]
1168 source_api_version = source_info["recovery_api_version"]
1169 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001170 print("WARNING: generating edify script for a source that "
1171 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001172
Tao Bao481bab82017-12-21 11:23:09 -08001173 script = edify_generator.EdifyGenerator(
1174 source_api_version, target_info, fstab=source_info["fstab"])
1175
1176 if target_info.oem_props or source_info.oem_props:
1177 if not OPTIONS.oem_no_mount:
1178 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001179
Tao Baodf3a48b2018-01-10 16:30:43 -08001180 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001181
Geremy Condra36bd3652014-02-06 19:45:10 -08001182 device_specific = common.DeviceSpecificParams(
1183 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001184 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001185 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001186 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001187 output_zip=output_zip,
1188 script=script,
1189 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001190 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001191
Geremy Condra36bd3652014-02-06 19:45:10 -08001192 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001193 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001194 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001195 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001196 updating_boot = (not OPTIONS.two_step and
1197 (source_boot.data != target_boot.data))
1198
Geremy Condra36bd3652014-02-06 19:45:10 -08001199 target_recovery = common.GetBootableImage(
1200 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001201
Tao Baoe709b092018-02-07 12:40:00 -08001202 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1203 # shared blocks (i.e. some blocks will show up in multiple files' block
1204 # list). We can only allocate such shared blocks to the first "owner", and
1205 # disable imgdiff for all later occurrences.
1206 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1207 target_info.get('ext4_share_dup_blocks') == "true")
1208 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1209 allow_shared_blocks)
1210 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
1211 allow_shared_blocks)
Tao Baodd2a5892015-03-12 12:32:37 -07001212
Tao Bao0582cb62017-12-21 11:47:01 -08001213 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001214 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001215 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001216
Tao Baof8acad12016-07-07 09:09:58 -07001217 # Check the first block of the source system partition for remount R/W only
1218 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001219 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001220 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001221 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1222 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1223 # b) the blocks listed in block map may not contain all the bytes for a given
1224 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001225 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001226 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1227 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001228 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001229 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001230 version=blockimgdiff_version,
1231 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001232
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001233 if HasVendorPartition(target_zip):
1234 if not HasVendorPartition(source_zip):
1235 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001236 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1237 allow_shared_blocks)
1238 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip,
1239 allow_shared_blocks)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001240
1241 # Check first block of vendor partition for remount R/W only if
1242 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001243 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001244 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001245 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001246 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001247 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001248 version=blockimgdiff_version,
1249 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001250 else:
1251 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001252
Tao Baobcd1d162017-08-26 13:10:26 -07001253 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001254 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001255
Tao Bao481bab82017-12-21 11:23:09 -08001256 # Assertions (e.g. device properties check).
1257 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001258 device_specific.IncrementalOTA_Assertions()
1259
1260 # Two-step incremental package strategy (in chronological order,
1261 # which is *not* the order in which the generated script has
1262 # things):
1263 #
1264 # if stage is not "2/3" or "3/3":
1265 # do verification on current system
1266 # write recovery image to boot partition
1267 # set stage to "2/3"
1268 # reboot to boot partition and restart recovery
1269 # else if stage is "2/3":
1270 # write recovery image to recovery partition
1271 # set stage to "3/3"
1272 # reboot to recovery partition and restart recovery
1273 # else:
1274 # (stage must be "3/3")
1275 # perform update:
1276 # patch system files, etc.
1277 # force full install of new boot image
1278 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001279 # complete script normally
1280 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001281
1282 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001283 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001284 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001285 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001286 assert fs.fs_type.upper() == "EMMC", \
1287 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001288 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001289 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1290 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001291if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001292""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001293
1294 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1295 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001296 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001297 script.WriteRawImage("/recovery", "recovery.img")
1298 script.AppendExtra("""
1299set_stage("%(bcb_dev)s", "3/3");
1300reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001301else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001302""" % bcb_dev)
1303
Tao Baod42e97e2016-11-30 12:11:57 -08001304 # Stage 1/3: (a) Verify the current system.
1305 script.Comment("Stage 1/3")
1306
Tao Bao6c55a8a2015-04-08 15:30:27 -07001307 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001308 script.Print("Source: {}".format(source_info.fingerprint))
1309 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001310
Geremy Condra36bd3652014-02-06 19:45:10 -08001311 script.Print("Verifying current system...")
1312
1313 device_specific.IncrementalOTA_VerifyBegin()
1314
Tao Bao481bab82017-12-21 11:23:09 -08001315 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001316
Tao Baod8d14be2016-02-04 14:26:02 -08001317 # Check the required cache size (i.e. stashed blocks).
1318 size = []
1319 if system_diff:
1320 size.append(system_diff.required_cache)
1321 if vendor_diff:
1322 size.append(vendor_diff.required_cache)
1323
Geremy Condra36bd3652014-02-06 19:45:10 -08001324 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001325 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001326 d = common.Difference(target_boot, source_boot)
1327 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001328 if d is None:
1329 include_full_boot = True
1330 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1331 else:
1332 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001333
Tao Bao89fbb0f2017-01-10 10:47:58 -08001334 print("boot target: %d source: %d diff: %d" % (
1335 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001336
Doug Zongkerf8340082014-08-05 10:39:37 -07001337 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001338
Doug Zongkerf8340082014-08-05 10:39:37 -07001339 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1340 (boot_type, boot_device,
1341 source_boot.size, source_boot.sha1,
1342 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001343 size.append(target_boot.size)
1344
1345 if size:
1346 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001347
1348 device_specific.IncrementalOTA_VerifyEnd()
1349
1350 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001351 # Stage 1/3: (b) Write recovery image to /boot.
1352 _WriteRecoveryImageToBoot(script, output_zip)
1353
Geremy Condra36bd3652014-02-06 19:45:10 -08001354 script.AppendExtra("""
1355set_stage("%(bcb_dev)s", "2/3");
1356reboot_now("%(bcb_dev)s", "");
1357else
1358""" % bcb_dev)
1359
Tao Baod42e97e2016-11-30 12:11:57 -08001360 # Stage 3/3: Make changes.
1361 script.Comment("Stage 3/3")
1362
Jesse Zhao75bcea02015-01-06 10:59:53 -08001363 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001364 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001365 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001366 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001367
Geremy Condra36bd3652014-02-06 19:45:10 -08001368 script.Comment("---- start making changes here ----")
1369
1370 device_specific.IncrementalOTA_InstallBegin()
1371
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001372 system_diff.WriteScript(script, output_zip,
1373 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001374
Doug Zongkerfc44a512014-08-26 13:10:25 -07001375 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001376 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001377
1378 if OPTIONS.two_step:
1379 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1380 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001381 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001382
1383 if not OPTIONS.two_step:
1384 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001385 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001386 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001387 script.Print("Installing boot image...")
1388 script.WriteRawImage("/boot", "boot.img")
1389 else:
1390 # Produce the boot image by applying a patch to the current
1391 # contents of the boot partition, and write it back to the
1392 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001393 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001394 script.Print("Patching boot image...")
1395 script.ShowProgress(0.1, 10)
1396 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1397 % (boot_type, boot_device,
1398 source_boot.size, source_boot.sha1,
1399 target_boot.size, target_boot.sha1),
1400 "-",
1401 target_boot.size, target_boot.sha1,
1402 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001403 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001404 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001405
1406 # Do device-specific installation (eg, write radio image).
1407 device_specific.IncrementalOTA_InstallEnd()
1408
1409 if OPTIONS.extra_script is not None:
1410 script.AppendExtra(OPTIONS.extra_script)
1411
Doug Zongker922206e2014-03-04 13:16:24 -08001412 if OPTIONS.wipe_user_data:
1413 script.Print("Erasing user data...")
1414 script.FormatPartition("/data")
1415
Geremy Condra36bd3652014-02-06 19:45:10 -08001416 if OPTIONS.two_step:
1417 script.AppendExtra("""
1418set_stage("%(bcb_dev)s", "");
1419endif;
1420endif;
1421""" % bcb_dev)
1422
1423 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001424 # For downgrade OTAs, we prefer to use the update-binary in the source
1425 # build that is actually newer than the one in the target build.
1426 if OPTIONS.downgrade:
1427 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1428 else:
1429 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001430 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001431 WriteMetadata(metadata, output_zip)
1432
Doug Zongker32b527d2014-03-04 10:03:02 -08001433
Tao Bao15a146a2018-02-21 16:06:59 -08001434def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001435 """Returns a target-files.zip file for generating secondary payload.
1436
1437 Although the original target-files.zip already contains secondary slot
1438 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1439 ones without _other suffix. Note that we cannot instead modify the names in
1440 META/ab_partitions.txt, because there are no matching partitions on device.
1441
1442 For the partitions that don't have secondary images, the ones for primary
1443 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1444 bootloader images in the inactive slot.
1445
1446 Args:
1447 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001448 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001449
1450 Returns:
1451 The filename of the target-files.zip for generating secondary payload.
1452 """
1453 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1454 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1455
Tao Baodba59ee2018-01-09 13:21:02 -08001456 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
1457 with zipfile.ZipFile(input_file, 'r') as input_zip:
1458 infolist = input_zip.infolist()
1459
1460 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001461 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1462 if info.filename == 'IMAGES/system_other.img':
1463 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1464
1465 # Primary images and friends need to be skipped explicitly.
1466 elif info.filename in ('IMAGES/system.img',
1467 'IMAGES/system.map'):
1468 pass
1469
Tao Bao15a146a2018-02-21 16:06:59 -08001470 # Skip copying the postinstall config if requested.
1471 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1472 pass
1473
Tao Baof7140c02018-01-30 17:09:24 -08001474 elif info.filename.startswith(('META/', 'IMAGES/')):
1475 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1476
Tao Baof7140c02018-01-30 17:09:24 -08001477 common.ZipClose(target_zip)
1478
1479 return target_file
1480
1481
Tao Bao15a146a2018-02-21 16:06:59 -08001482def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1483 """Returns a target-files.zip that's not containing postinstall_config.txt.
1484
1485 This allows brillo_update_payload script to skip writing all the postinstall
1486 hooks in the generated payload. The input target-files.zip file will be
1487 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1488 contain the postinstall_config.txt entry, the input file will be returned.
1489
1490 Args:
1491 input_file: The input target-files.zip filename.
1492
1493 Returns:
1494 The filename of target-files.zip that doesn't contain postinstall config.
1495 """
1496 # We should only make a copy if postinstall_config entry exists.
1497 with zipfile.ZipFile(input_file, 'r') as input_zip:
1498 if POSTINSTALL_CONFIG not in input_zip.namelist():
1499 return input_file
1500
1501 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1502 shutil.copyfile(input_file, target_file)
1503 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1504 return target_file
1505
1506
Tao Baoc098e9e2016-01-07 13:03:56 -08001507def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1508 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001509 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001510 # Stage the output zip package for package signing.
Tao Baoa652c002018-03-01 19:31:38 -08001511 staging_file = common.MakeTempFile(suffix='.zip')
1512 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001513 compression=zipfile.ZIP_DEFLATED)
1514
Tao Bao481bab82017-12-21 11:23:09 -08001515 if source_file is not None:
1516 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1517 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1518 else:
1519 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1520 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001521
Tao Bao481bab82017-12-21 11:23:09 -08001522 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001523 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001524
Tao Bao15a146a2018-02-21 16:06:59 -08001525 if OPTIONS.skip_postinstall:
1526 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1527
Tao Bao40b18822018-01-30 18:19:04 -08001528 # Generate payload.
1529 payload = Payload()
1530
1531 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001532 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001533 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001534 else:
1535 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001536 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001537
Tao Bao40b18822018-01-30 18:19:04 -08001538 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001539
Tao Bao40b18822018-01-30 18:19:04 -08001540 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001541 payload_signer = PayloadSigner()
1542 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001543
Tao Bao40b18822018-01-30 18:19:04 -08001544 # Write the payload into output zip.
1545 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001546
Tao Baof7140c02018-01-30 17:09:24 -08001547 # Generate and include the secondary payload that installs secondary images
1548 # (e.g. system_other.img).
1549 if OPTIONS.include_secondary:
1550 # We always include a full payload for the secondary slot, even when
1551 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001552 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1553 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001554 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001555 secondary_payload.Generate(secondary_target_file,
1556 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001557 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001558 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001559
Tianjie Xucfa86222016-03-07 16:31:19 -08001560 # If dm-verity is supported for the device, copy contents of care_map
1561 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001562 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001563 if (target_info.get("verity") == "true" or
1564 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001565 care_map_path = "META/care_map.txt"
1566 namelist = target_zip.namelist()
1567 if care_map_path in namelist:
1568 care_map_data = target_zip.read(care_map_path)
Tao Bao40b18822018-01-30 18:19:04 -08001569 # In order to support streaming, care_map.txt needs to be packed as
1570 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001571 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001572 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001573 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001574 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001575
Tao Baobcd1d162017-08-26 13:10:26 -07001576 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001577 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001578
Tao Bao21803d32017-04-19 10:16:09 -07001579 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001580
Tao Baofe5b69a2018-03-02 09:47:43 -08001581 # We haven't written the metadata entry yet, which will be handled in
1582 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001583 common.ZipClose(output_zip)
1584
Tao Baod3fc38a2018-03-08 16:09:01 -08001585 needed_property_files = (
1586 StreamingPropertyFiles(),
1587 )
1588 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001589
Tao Baoc098e9e2016-01-07 13:03:56 -08001590
Doug Zongkereef39442009-04-02 12:14:19 -07001591def main(argv):
1592
1593 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001594 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001595 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001596 elif o in ("-i", "--incremental_from"):
1597 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001598 elif o == "--full_radio":
1599 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001600 elif o == "--full_bootloader":
1601 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001602 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001603 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001604 elif o == "--downgrade":
1605 OPTIONS.downgrade = True
1606 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001607 elif o == "--override_timestamp":
1608 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001609 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001610 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001611 elif o == "--oem_no_mount":
1612 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001613 elif o in ("-e", "--extra_script"):
1614 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001615 elif o in ("-t", "--worker_threads"):
1616 if a.isdigit():
1617 OPTIONS.worker_threads = int(a)
1618 else:
1619 raise ValueError("Cannot parse value %r for option %r - only "
1620 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001621 elif o in ("-2", "--two_step"):
1622 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001623 elif o == "--include_secondary":
1624 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001625 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001626 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001627 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001628 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001629 elif o == "--block":
1630 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001631 elif o in ("-b", "--binary"):
1632 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001633 elif o == "--stash_threshold":
1634 try:
1635 OPTIONS.stash_threshold = float(a)
1636 except ValueError:
1637 raise ValueError("Cannot parse value %r for option %r - expecting "
1638 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001639 elif o == "--log_diff":
1640 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001641 elif o == "--payload_signer":
1642 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001643 elif o == "--payload_signer_args":
1644 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001645 elif o == "--extracted_input_target_files":
1646 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001647 elif o == "--skip_postinstall":
1648 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001649 else:
1650 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001651 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001652
1653 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001654 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001655 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001656 "package_key=",
1657 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001658 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001659 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001660 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001661 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001662 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001663 "extra_script=",
1664 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001665 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001666 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001667 "no_signing",
1668 "block",
1669 "binary=",
1670 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001671 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001672 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001673 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001674 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001675 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001676 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001677 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001678 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001679 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001680
1681 if len(args) != 2:
1682 common.Usage(__doc__)
1683 sys.exit(1)
1684
Tao Bao5d182562016-02-23 11:38:39 -08001685 if OPTIONS.downgrade:
1686 # Sanity check to enforce a data wipe.
1687 if not OPTIONS.wipe_user_data:
1688 raise ValueError("Cannot downgrade without a data wipe")
1689
1690 # We should only allow downgrading incrementals (as opposed to full).
1691 # Otherwise the device may go back from arbitrary build with this full
1692 # OTA package.
1693 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001694 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001695
Tao Bao3e6161a2017-02-28 11:48:48 -08001696 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1697 "Cannot have --downgrade AND --override_timestamp both"
1698
Tao Bao2db13852018-01-08 22:28:57 -08001699 # Load the build info dicts from the zip directly or the extracted input
1700 # directory. We don't need to unzip the entire target-files zips, because they
1701 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1702 # When loading the info dicts, we don't need to provide the second parameter
1703 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1704 # some properties with their actual paths, such as 'selinux_fc',
1705 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001706 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001707 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001708 else:
Tao Bao2db13852018-01-08 22:28:57 -08001709 with zipfile.ZipFile(args[0], 'r') as input_zip:
1710 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001711
Tao Bao2db13852018-01-08 22:28:57 -08001712 if OPTIONS.verbose:
1713 print("--- target info ---")
1714 common.DumpInfoDict(OPTIONS.info_dict)
1715
1716 # Load the source build dict if applicable.
1717 if OPTIONS.incremental_source is not None:
1718 OPTIONS.target_info_dict = OPTIONS.info_dict
1719 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1720 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1721
1722 if OPTIONS.verbose:
1723 print("--- source info ---")
1724 common.DumpInfoDict(OPTIONS.source_info_dict)
1725
1726 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001727 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1728
Tao Baoc098e9e2016-01-07 13:03:56 -08001729 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1730
Christian Oderf63e2cd2017-05-01 22:30:15 +02001731 # Use the default key to sign the package if not specified with package_key.
1732 # package_keys are needed on ab_updates, so always define them if an
1733 # ab_update is getting created.
1734 if not OPTIONS.no_signing or ab_update:
1735 if OPTIONS.package_key is None:
1736 OPTIONS.package_key = OPTIONS.info_dict.get(
1737 "default_system_dev_certificate",
1738 "build/target/product/security/testkey")
1739 # Get signing keys
1740 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1741
Tao Baoc098e9e2016-01-07 13:03:56 -08001742 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001743 WriteABOTAPackageWithBrilloScript(
1744 target_file=args[0],
1745 output_file=args[1],
1746 source_file=OPTIONS.incremental_source)
1747
Tao Bao89fbb0f2017-01-10 10:47:58 -08001748 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001749 return
1750
Tao Bao2db13852018-01-08 22:28:57 -08001751 # Sanity check the loaded info dicts first.
1752 if OPTIONS.info_dict.get("no_recovery") == "true":
1753 raise common.ExternalError(
1754 "--- target build has specified no recovery ---")
1755
1756 # Non-A/B OTAs rely on /cache partition to store temporary files.
1757 cache_size = OPTIONS.info_dict.get("cache_size")
1758 if cache_size is None:
1759 print("--- can't determine the cache partition size ---")
1760 OPTIONS.cache_size = cache_size
1761
Doug Zongker1c390a22009-05-14 19:06:36 -07001762 if OPTIONS.extra_script is not None:
1763 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1764
Dan Willemsencea5cd22017-03-21 14:44:27 -07001765 if OPTIONS.extracted_input is not None:
1766 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001767 else:
1768 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001769 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001770 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001771
Tao Bao2db13852018-01-08 22:28:57 -08001772 # If the caller explicitly specified the device-specific extensions path via
1773 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1774 # is present in the target target_files. Otherwise, take the path of the file
1775 # from 'tool_extensions' in the info dict and look for that in the local
1776 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001777 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001778 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1779 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001780 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001781 OPTIONS.device_specific = from_input
1782 else:
Tao Bao2db13852018-01-08 22:28:57 -08001783 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001784
Doug Zongker37974732010-09-16 17:44:38 -07001785 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001786 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001787
Tao Bao767e3ac2015-11-10 12:19:19 -08001788 # Set up the output zip. Create a temporary zip file if signing is needed.
1789 if OPTIONS.no_signing:
1790 if os.path.exists(args[1]):
1791 os.unlink(args[1])
1792 output_zip = zipfile.ZipFile(args[1], "w",
1793 compression=zipfile.ZIP_DEFLATED)
1794 else:
1795 temp_zip_file = tempfile.NamedTemporaryFile()
1796 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1797 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001798
Tao Bao767e3ac2015-11-10 12:19:19 -08001799 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001800 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08001801 with zipfile.ZipFile(args[0], 'r') as input_zip:
1802 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001803
Tao Bao32b80dc2018-01-08 22:50:47 -08001804 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001805 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001806 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001807 OPTIONS.source_tmp = common.UnzipTemp(
1808 OPTIONS.incremental_source, UNZIP_PATTERN)
1809 with zipfile.ZipFile(args[0], 'r') as input_zip, \
1810 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1811 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Bao32b80dc2018-01-08 22:50:47 -08001812
1813 if OPTIONS.log_diff:
1814 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001815 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001816 target_files_diff.recursiveDiff(
1817 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001818
Tao Bao767e3ac2015-11-10 12:19:19 -08001819 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001820
Tao Bao767e3ac2015-11-10 12:19:19 -08001821 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001822 if not OPTIONS.no_signing:
1823 SignOutput(temp_zip_file.name, args[1])
1824 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001825
Tao Bao89fbb0f2017-01-10 10:47:58 -08001826 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001827
1828
1829if __name__ == '__main__':
1830 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001831 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001832 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001833 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001834 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001835 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001836 finally:
1837 common.Cleanup()