blob: b6c26bfaf1c199e9b1030e643a1c808e92f28cde [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
27 to an older one (based on timestamp comparison). "post-timestamp" will be
28 replaced by "ota-downgrade=yes" in the metadata file. A data wipe will
29 always be enforced, so "ota-wipe=yes" will also be included in the
30 metadata file. The update-binary in the source build will be used in the
31 OTA package, unless --binary flag is specified. Please also check the doc
32 for --override_timestamp below.
33
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
41 META/misc_info.txt, or "build/target/product/security/testkey" if that
42 value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
49 to an older one (based on timestamp comparison), by overriding the
50 timestamp in package metadata. This differs from --downgrade flag: we know
51 for sure this is NOT an actual downgrade case, but two builds are cut in a
52 reverse order. A legit use case is that we cut a new build C (after having
53 A and B), but want to enfore an update path of A -> C -> B. Specifying
54 --downgrade may not help since that would enforce a data wipe for C -> B
55 update. The value of "post-timestamp" will be set to the newer timestamp
56 plus one, so that the package can be pushed and applied.
Doug Zongkereef39442009-04-02 12:14:19 -070057
Tao Bao30df8b42018-04-23 15:32:53 -070058 --wipe_user_data
59 Generate an OTA package that will wipe the user data partition when
60 installed.
61
62Non-A/B OTA specific options
63
64 -b (--binary) <file>
65 Use the given binary as the update-binary in the output package, instead
66 of the binary in the build's target_files. Use for development only.
67
68 --block
69 Generate a block-based OTA for non-A/B device. We have deprecated the
70 support for file-based OTA since O. Block-based OTA will be used by
71 default for all non-A/B devices. Keeping this flag here to not break
72 existing callers.
73
74 -e (--extra_script) <file>
75 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070076
leozwangaa6c1a12015-08-14 10:57:58 -070077 --full_bootloader
78 Similar to --full_radio. When generating an incremental OTA, always
79 include a full copy of bootloader image.
80
Tao Bao30df8b42018-04-23 15:32:53 -070081 --full_radio
82 When generating an incremental OTA, always include a full copy of radio
83 image. This option is only meaningful when -i is specified, because a full
84 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -070085
Tao Bao30df8b42018-04-23 15:32:53 -070086 --log_diff <file>
87 Generate a log file that shows the differences in the source and target
88 builds for an incremental package. This option is only meaningful when -i
89 is specified.
90
91 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080092 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -080093 properties on the OEM partition of the intended device. Multiple expected
94 values can be used by providing multiple files. Only the first dict will
95 be used to compute fingerprint, while the rest will be used to assert
96 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080097
Tao Bao8608cde2016-02-25 19:49:55 -080098 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -070099 For devices with OEM-specific properties but without an OEM partition, do
100 not mount the OEM partition in the updater-script. This should be very
101 rarely used, since it's expected to have a dedicated OEM partition for
102 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800103
Tao Bao30df8b42018-04-23 15:32:53 -0700104 --stash_threshold <float>
105 Specify the threshold that will be used to compute the maximum allowed
106 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700107
Tao Bao30df8b42018-04-23 15:32:53 -0700108 -t (--worker_threads) <int>
109 Specify the number of worker-threads that will be used when generating
110 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800111
Tao Bao30df8b42018-04-23 15:32:53 -0700112 --verify
113 Verify the checksums of the updated system and vendor (if any) partitions.
114 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700115
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800116 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700117 Generate a 'two-step' OTA package, where recovery is updated first, so
118 that any changes made to the system partition are done using the new
119 recovery (new kernel, etc.).
120
121A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800122
Tao Baof7140c02018-01-30 17:09:24 -0800123 --include_secondary
124 Additionally include the payload for secondary slot images (default:
125 False). Only meaningful when generating A/B OTAs.
126
127 By default, an A/B OTA package doesn't contain the images for the
128 secondary slot (e.g. system_other.img). Specifying this flag allows
129 generating a separate payload that will install secondary slot images.
130
131 Such a package needs to be applied in a two-stage manner, with a reboot
132 in-between. During the first stage, the updater applies the primary
133 payload only. Upon finishing, it reboots the device into the newly updated
134 slot. It then continues to install the secondary payload to the inactive
135 slot, but without switching the active slot at the end (needs the matching
136 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
137
138 Due to the special install procedure, the secondary payload will be always
139 generated as a full payload.
140
Tao Baodea0f8b2016-06-20 17:55:06 -0700141 --payload_signer <signer>
142 Specify the signer when signing the payload and metadata for A/B OTAs.
143 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
144 with the package private key. If the private key cannot be accessed
145 directly, a payload signer that knows how to do that should be specified.
146 The signer will be supplied with "-inkey <path_to_key>",
147 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700148
149 --payload_signer_args <args>
150 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800151
152 --skip_postinstall
153 Skip the postinstall hooks when generating an A/B OTA package (default:
154 False). Note that this discards ALL the hooks, including non-optional
155 ones. Should only be used if caller knows it's safe to do so (e.g. all the
156 postinstall work is to dexopt apps and a data wipe will happen immediately
157 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700158"""
159
Tao Bao89fbb0f2017-01-10 10:47:58 -0800160from __future__ import print_function
161
Doug Zongkerfc44a512014-08-26 13:10:25 -0700162import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800163import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700164import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800165import shutil
Tao Baob6304672018-03-08 16:28:33 -0800166import struct
Tao Bao481bab82017-12-21 11:23:09 -0800167import subprocess
168import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700169import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700170import zipfile
171
172import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700173import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -0700174
Tao Bao481bab82017-12-21 11:23:09 -0800175if sys.hexversion < 0x02070000:
176 print("Python 2.7 or newer is required.", file=sys.stderr)
177 sys.exit(1)
178
179
Doug Zongkereef39442009-04-02 12:14:19 -0700180OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700181OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700182OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700183OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700184OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700185OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800186OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800187OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700188OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700189OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
190if OPTIONS.worker_threads == 0:
191 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800192OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800193OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900194OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800195OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800196OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700197OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800198OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700199OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700200OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700201# Stash size cannot exceed cache_size * threshold.
202OPTIONS.cache_size = None
203OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800204OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700205OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700206OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700207OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200208OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800209OPTIONS.skip_postinstall = False
210
Tao Bao8dcf7382015-05-21 14:09:49 -0700211
Tao Bao2dd1c482017-02-03 16:49:39 -0800212METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800213POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800214UNZIP_PATTERN = ['IMAGES/*', 'META/*']
215
Tao Bao2dd1c482017-02-03 16:49:39 -0800216
Tao Bao481bab82017-12-21 11:23:09 -0800217class BuildInfo(object):
218 """A class that holds the information for a given build.
219
220 This class wraps up the property querying for a given source or target build.
221 It abstracts away the logic of handling OEM-specific properties, and caches
222 the commonly used properties such as fingerprint.
223
224 There are two types of info dicts: a) build-time info dict, which is generated
225 at build time (i.e. included in a target_files zip); b) OEM info dict that is
226 specified at package generation time (via command line argument
227 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
228 having "oem_fingerprint_properties" in build-time info dict), all the queries
229 would be answered based on build-time info dict only. Otherwise if using
230 OEM-specific properties, some of them will be calculated from two info dicts.
231
232 Users can query properties similarly as using a dict() (e.g. info['fstab']),
233 or to query build properties via GetBuildProp() or GetVendorBuildProp().
234
235 Attributes:
236 info_dict: The build-time info dict.
237 is_ab: Whether it's a build that uses A/B OTA.
238 oem_dicts: A list of OEM dicts.
239 oem_props: A list of OEM properties that should be read from OEM dicts; None
240 if the build doesn't use any OEM-specific property.
241 fingerprint: The fingerprint of the build, which would be calculated based
242 on OEM properties if applicable.
243 device: The device name, which could come from OEM dicts if applicable.
244 """
245
246 def __init__(self, info_dict, oem_dicts):
247 """Initializes a BuildInfo instance with the given dicts.
248
249 Arguments:
250 info_dict: The build-time info dict.
251 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
252 that it always uses the first dict to calculate the fingerprint or the
253 device name. The rest would be used for asserting OEM properties only
254 (e.g. one package can be installed on one of these devices).
255 """
256 self.info_dict = info_dict
257 self.oem_dicts = oem_dicts
258
259 self._is_ab = info_dict.get("ab_update") == "true"
260 self._oem_props = info_dict.get("oem_fingerprint_properties")
261
262 if self._oem_props:
263 assert oem_dicts, "OEM source required for this build"
264
265 # These two should be computed only after setting self._oem_props.
266 self._device = self.GetOemProperty("ro.product.device")
267 self._fingerprint = self.CalculateFingerprint()
268
269 @property
270 def is_ab(self):
271 return self._is_ab
272
273 @property
274 def device(self):
275 return self._device
276
277 @property
278 def fingerprint(self):
279 return self._fingerprint
280
281 @property
282 def oem_props(self):
283 return self._oem_props
284
285 def __getitem__(self, key):
286 return self.info_dict[key]
287
288 def get(self, key, default=None):
289 return self.info_dict.get(key, default)
290
291 def GetBuildProp(self, prop):
292 """Returns the inquired build property."""
293 try:
294 return self.info_dict.get("build.prop", {})[prop]
295 except KeyError:
296 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
297
298 def GetVendorBuildProp(self, prop):
299 """Returns the inquired vendor build property."""
300 try:
301 return self.info_dict.get("vendor.build.prop", {})[prop]
302 except KeyError:
303 raise common.ExternalError(
304 "couldn't find %s in vendor.build.prop" % (prop,))
305
306 def GetOemProperty(self, key):
307 if self.oem_props is not None and key in self.oem_props:
308 return self.oem_dicts[0][key]
309 return self.GetBuildProp(key)
310
311 def CalculateFingerprint(self):
312 if self.oem_props is None:
313 return self.GetBuildProp("ro.build.fingerprint")
314 return "%s/%s/%s:%s" % (
315 self.GetOemProperty("ro.product.brand"),
316 self.GetOemProperty("ro.product.name"),
317 self.GetOemProperty("ro.product.device"),
318 self.GetBuildProp("ro.build.thumbprint"))
319
320 def WriteMountOemScript(self, script):
321 assert self.oem_props is not None
322 recovery_mount_options = self.info_dict.get("recovery_mount_options")
323 script.Mount("/oem", recovery_mount_options)
324
325 def WriteDeviceAssertions(self, script, oem_no_mount):
326 # Read the property directly if not using OEM properties.
327 if not self.oem_props:
328 script.AssertDevice(self.device)
329 return
330
331 # Otherwise assert OEM properties.
332 if not self.oem_dicts:
333 raise common.ExternalError(
334 "No OEM file provided to answer expected assertions")
335
336 for prop in self.oem_props.split():
337 values = []
338 for oem_dict in self.oem_dicts:
339 if prop in oem_dict:
340 values.append(oem_dict[prop])
341 if not values:
342 raise common.ExternalError(
343 "The OEM file is missing the property %s" % (prop,))
344 script.AssertOemProperty(prop, values, oem_no_mount)
345
346
Tao Baofabe0832018-01-17 15:52:28 -0800347class PayloadSigner(object):
348 """A class that wraps the payload signing works.
349
350 When generating a Payload, hashes of the payload and metadata files will be
351 signed with the device key, either by calling an external payload signer or
352 by calling openssl with the package key. This class provides a unified
353 interface, so that callers can just call PayloadSigner.Sign().
354
355 If an external payload signer has been specified (OPTIONS.payload_signer), it
356 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
357 that the signing key should be provided as part of the payload_signer_args.
358 Otherwise without an external signer, it uses the package key
359 (OPTIONS.package_key) and calls openssl for the signing works.
360 """
361
362 def __init__(self):
363 if OPTIONS.payload_signer is None:
364 # Prepare the payload signing key.
365 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
366 pw = OPTIONS.key_passwords[OPTIONS.package_key]
367
368 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
369 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
370 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
371 cmd.extend(["-out", signing_key])
372
373 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
374 stderr=subprocess.STDOUT)
375 stdoutdata, _ = get_signing_key.communicate()
376 assert get_signing_key.returncode == 0, \
377 "Failed to get signing key: {}".format(stdoutdata)
378
379 self.signer = "openssl"
380 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
381 "-pkeyopt", "digest:sha256"]
382 else:
383 self.signer = OPTIONS.payload_signer
384 self.signer_args = OPTIONS.payload_signer_args
385
386 def Sign(self, in_file):
387 """Signs the given input file. Returns the output filename."""
388 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
389 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
390 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
391 stdoutdata, _ = signing.communicate()
392 assert signing.returncode == 0, \
393 "Failed to sign the input file: {}".format(stdoutdata)
394 return out_file
395
396
Tao Baoc7b403a2018-01-30 18:19:04 -0800397class Payload(object):
398 """Manages the creation and the signing of an A/B OTA Payload."""
399
400 PAYLOAD_BIN = 'payload.bin'
401 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800402 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
403 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Baoc7b403a2018-01-30 18:19:04 -0800404
Tao Bao667ff572018-02-10 00:02:40 -0800405 def __init__(self, secondary=False):
406 """Initializes a Payload instance.
407
408 Args:
409 secondary: Whether it's generating a secondary payload (default: False).
410 """
Tao Baoc7b403a2018-01-30 18:19:04 -0800411 # The place where the output from the subprocess should go.
412 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
413 self.payload_file = None
414 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800415 self.secondary = secondary
Tao Baoc7b403a2018-01-30 18:19:04 -0800416
417 def Generate(self, target_file, source_file=None, additional_args=None):
418 """Generates a payload from the given target-files zip(s).
419
420 Args:
421 target_file: The filename of the target build target-files zip.
422 source_file: The filename of the source build target-files zip; or None if
423 generating a full OTA.
424 additional_args: A list of additional args that should be passed to
425 brillo_update_payload script; or None.
426 """
427 if additional_args is None:
428 additional_args = []
429
430 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
431 cmd = ["brillo_update_payload", "generate",
432 "--payload", payload_file,
433 "--target_image", target_file]
434 if source_file is not None:
435 cmd.extend(["--source_image", source_file])
436 cmd.extend(additional_args)
437 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
438 stdoutdata, _ = p.communicate()
439 assert p.returncode == 0, \
440 "brillo_update_payload generate failed: {}".format(stdoutdata)
441
442 self.payload_file = payload_file
443 self.payload_properties = None
444
445 def Sign(self, payload_signer):
446 """Generates and signs the hashes of the payload and metadata.
447
448 Args:
449 payload_signer: A PayloadSigner() instance that serves the signing work.
450
451 Raises:
452 AssertionError: On any failure when calling brillo_update_payload script.
453 """
454 assert isinstance(payload_signer, PayloadSigner)
455
456 # 1. Generate hashes of the payload and metadata files.
457 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
458 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
459 cmd = ["brillo_update_payload", "hash",
460 "--unsigned_payload", self.payload_file,
461 "--signature_size", "256",
462 "--metadata_hash_file", metadata_sig_file,
463 "--payload_hash_file", payload_sig_file]
464 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
465 p1.communicate()
466 assert p1.returncode == 0, "brillo_update_payload hash failed"
467
468 # 2. Sign the hashes.
469 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
470 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
471
472 # 3. Insert the signatures back into the payload file.
473 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
474 suffix=".bin")
475 cmd = ["brillo_update_payload", "sign",
476 "--unsigned_payload", self.payload_file,
477 "--payload", signed_payload_file,
478 "--signature_size", "256",
479 "--metadata_signature_file", signed_metadata_sig_file,
480 "--payload_signature_file", signed_payload_sig_file]
481 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
482 p1.communicate()
483 assert p1.returncode == 0, "brillo_update_payload sign failed"
484
485 # 4. Dump the signed payload properties.
486 properties_file = common.MakeTempFile(prefix="payload-properties-",
487 suffix=".txt")
488 cmd = ["brillo_update_payload", "properties",
489 "--payload", signed_payload_file,
490 "--properties_file", properties_file]
491 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
492 p1.communicate()
493 assert p1.returncode == 0, "brillo_update_payload properties failed"
494
Tao Bao667ff572018-02-10 00:02:40 -0800495 if self.secondary:
496 with open(properties_file, "a") as f:
497 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
498
Tao Baoc7b403a2018-01-30 18:19:04 -0800499 if OPTIONS.wipe_user_data:
500 with open(properties_file, "a") as f:
501 f.write("POWERWASH=1\n")
502
503 self.payload_file = signed_payload_file
504 self.payload_properties = properties_file
505
Tao Bao667ff572018-02-10 00:02:40 -0800506 def WriteToZip(self, output_zip):
Tao Baoc7b403a2018-01-30 18:19:04 -0800507 """Writes the payload to the given zip.
508
509 Args:
510 output_zip: The output ZipFile instance.
511 """
512 assert self.payload_file is not None
513 assert self.payload_properties is not None
514
Tao Bao667ff572018-02-10 00:02:40 -0800515 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800516 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
517 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
518 else:
519 payload_arcname = Payload.PAYLOAD_BIN
520 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
521
Tao Baoc7b403a2018-01-30 18:19:04 -0800522 # Add the signed payload file and properties into the zip. In order to
523 # support streaming, we pack them as ZIP_STORED. So these entries can be
524 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800525 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Baoc7b403a2018-01-30 18:19:04 -0800526 compress_type=zipfile.ZIP_STORED)
527 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800528 arcname=payload_properties_arcname,
Tao Baoc7b403a2018-01-30 18:19:04 -0800529 compress_type=zipfile.ZIP_STORED)
530
531
Doug Zongkereef39442009-04-02 12:14:19 -0700532def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200533 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700534
Doug Zongker951495f2009-08-14 12:44:19 -0700535 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
536 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700537
538
Tao Bao481bab82017-12-21 11:23:09 -0800539def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800540 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800541 if not oem_source:
542 return None
543
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800544 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800545 for oem_file in oem_source:
546 with open(oem_file) as fp:
547 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800548 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700549
Doug Zongkereef39442009-04-02 12:14:19 -0700550
Tao Baod42e97e2016-11-30 12:11:57 -0800551def _WriteRecoveryImageToBoot(script, output_zip):
552 """Find and write recovery image to /boot in two-step OTA.
553
554 In two-step OTAs, we write recovery image to /boot as the first step so that
555 we can reboot to there and install a new recovery image to /recovery.
556 A special "recovery-two-step.img" will be preferred, which encodes the correct
557 path of "/boot". Otherwise the device may show "device is corrupt" message
558 when booting into /boot.
559
560 Fall back to using the regular recovery.img if the two-step recovery image
561 doesn't exist. Note that rebuilding the special image at this point may be
562 infeasible, because we don't have the desired boot signer and keys when
563 calling ota_from_target_files.py.
564 """
565
566 recovery_two_step_img_name = "recovery-two-step.img"
567 recovery_two_step_img_path = os.path.join(
568 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
569 if os.path.exists(recovery_two_step_img_path):
570 recovery_two_step_img = common.GetBootableImage(
571 recovery_two_step_img_name, recovery_two_step_img_name,
572 OPTIONS.input_tmp, "RECOVERY")
573 common.ZipWriteStr(
574 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800575 print("two-step package: using %s in stage 1/3" % (
576 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800577 script.WriteRawImage("/boot", recovery_two_step_img_name)
578 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800579 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800580 # The "recovery.img" entry has been written into package earlier.
581 script.WriteRawImage("/boot", "recovery.img")
582
583
Doug Zongkerc9253822014-02-04 12:17:58 -0800584def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700585 namelist = [name for name in target_files_zip.namelist()]
586 return ("SYSTEM/recovery-from-boot.p" in namelist or
587 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700588
Tao Bao457cbf62017-03-06 09:56:01 -0800589
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700590def HasVendorPartition(target_files_zip):
591 try:
592 target_files_zip.getinfo("VENDOR/")
593 return True
594 except KeyError:
595 return False
596
Tao Bao457cbf62017-03-06 09:56:01 -0800597
Tao Bao481bab82017-12-21 11:23:09 -0800598def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700599 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800600 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700601
602
Tao Bao481bab82017-12-21 11:23:09 -0800603def WriteFingerprintAssertion(script, target_info, source_info):
604 source_oem_props = source_info.oem_props
605 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700606
Tao Bao481bab82017-12-21 11:23:09 -0800607 if source_oem_props is None and target_oem_props is None:
608 script.AssertSomeFingerprint(
609 source_info.fingerprint, target_info.fingerprint)
610 elif source_oem_props is not None and target_oem_props is not None:
611 script.AssertSomeThumbprint(
612 target_info.GetBuildProp("ro.build.thumbprint"),
613 source_info.GetBuildProp("ro.build.thumbprint"))
614 elif source_oem_props is None and target_oem_props is not None:
615 script.AssertFingerprintOrThumbprint(
616 source_info.fingerprint,
617 target_info.GetBuildProp("ro.build.thumbprint"))
618 else:
619 script.AssertFingerprintOrThumbprint(
620 target_info.fingerprint,
621 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700622
Doug Zongkerfc44a512014-08-26 13:10:25 -0700623
Tao Bao481bab82017-12-21 11:23:09 -0800624def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
625 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700626 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700627
628 Metadata used for on-device compatibility verification is retrieved from
629 target_zip then added to compatibility.zip which is added to the output_zip
630 archive.
631
Tao Baobcd1d162017-08-26 13:10:26 -0700632 Compatibility archive should only be included for devices that have enabled
633 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700634
635 Args:
636 target_zip: Zip file containing the source files to be included for OTA.
637 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800638 target_info: The BuildInfo instance that holds the target build info.
639 source_info: The BuildInfo instance that holds the source build info, if
640 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700641 """
642
Tao Baobcd1d162017-08-26 13:10:26 -0700643 def AddCompatibilityArchive(system_updated, vendor_updated):
644 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700645
Tao Baobcd1d162017-08-26 13:10:26 -0700646 Args:
647 system_updated: If True, the system image will be updated and therefore
648 its metadata should be included.
649 vendor_updated: If True, the vendor image will be updated and therefore
650 its metadata should be included.
651 """
652 # Determine what metadata we need. Files are names relative to META/.
653 compatibility_files = []
654 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
655 system_metadata = ("system_manifest.xml", "system_matrix.xml")
656 if vendor_updated:
657 compatibility_files += vendor_metadata
658 if system_updated:
659 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700660
Tao Baobcd1d162017-08-26 13:10:26 -0700661 # Create new archive.
662 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800663 compatibility_archive_zip = zipfile.ZipFile(
664 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700665
Tao Baobcd1d162017-08-26 13:10:26 -0700666 # Add metadata.
667 for file_name in compatibility_files:
668 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700669
Tao Baobcd1d162017-08-26 13:10:26 -0700670 if target_file_name in target_zip.namelist():
671 data = target_zip.read(target_file_name)
672 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700673
Tao Baobcd1d162017-08-26 13:10:26 -0700674 # Ensure files are written before we copy into output_zip.
675 compatibility_archive_zip.close()
676
677 # Only add the archive if we have any compatibility info.
678 if compatibility_archive_zip.namelist():
679 common.ZipWrite(output_zip, compatibility_archive.name,
680 arcname="compatibility.zip",
681 compress_type=zipfile.ZIP_STORED)
682
683 # Will only proceed if the target has enabled the Treble support (as well as
684 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800685 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700686 return
687
688 # We don't support OEM thumbprint in Treble world (which calculates
689 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800690 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700691
692 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800693 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700694 AddCompatibilityArchive(True, True)
695 return
696
Tao Bao481bab82017-12-21 11:23:09 -0800697 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700698
Tao Bao481bab82017-12-21 11:23:09 -0800699 source_fp = source_info.fingerprint
700 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700701 system_updated = source_fp != target_fp
702
Tao Bao481bab82017-12-21 11:23:09 -0800703 source_fp_vendor = source_info.GetVendorBuildProp(
704 "ro.vendor.build.fingerprint")
705 target_fp_vendor = target_info.GetVendorBuildProp(
706 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700707 vendor_updated = source_fp_vendor != target_fp_vendor
708
709 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700710
711
Tao Baoc0746f42018-02-21 13:17:22 -0800712def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800713 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700714
Tao Bao481bab82017-12-21 11:23:09 -0800715 # We don't know what version it will be installed on top of. We expect the API
716 # just won't change very often. Similarly for fstab, it might have changed in
717 # the target build.
718 target_api_version = target_info["recovery_api_version"]
719 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700720
Tao Bao481bab82017-12-21 11:23:09 -0800721 if target_info.oem_props and not OPTIONS.oem_no_mount:
722 target_info.WriteMountOemScript(script)
723
Tao Baodf3a48b2018-01-10 16:30:43 -0800724 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700725
Tao Baoc0746f42018-02-21 13:17:22 -0800726 if not OPTIONS.no_signing:
727 staging_file = common.MakeTempFile(suffix='.zip')
728 else:
729 staging_file = output_file
730
731 output_zip = zipfile.ZipFile(
732 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
733
Doug Zongker05d3dea2009-06-22 11:32:31 -0700734 device_specific = common.DeviceSpecificParams(
735 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800736 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700737 output_zip=output_zip,
738 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700739 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700740 metadata=metadata,
741 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700742
Tao Bao457cbf62017-03-06 09:56:01 -0800743 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800744
Tao Bao481bab82017-12-21 11:23:09 -0800745 # Assertions (e.g. downgrade check, device properties check).
746 ts = target_info.GetBuildProp("ro.build.date.utc")
747 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700748 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700749
Tao Bao481bab82017-12-21 11:23:09 -0800750 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700751 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800752
753 # Two-step package strategy (in chronological order, which is *not*
754 # the order in which the generated script has things):
755 #
756 # if stage is not "2/3" or "3/3":
757 # write recovery image to boot partition
758 # set stage to "2/3"
759 # reboot to boot partition and restart recovery
760 # else if stage is "2/3":
761 # write recovery image to recovery partition
762 # set stage to "3/3"
763 # reboot to recovery partition and restart recovery
764 # else:
765 # (stage must be "3/3")
766 # set stage to ""
767 # do normal full package installation:
768 # wipe and install system, boot image, etc.
769 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700770 # complete script normally
771 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800772
773 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
774 OPTIONS.input_tmp, "RECOVERY")
775 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800776 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800777 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800778 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800779 assert fs.fs_type.upper() == "EMMC", \
780 "two-step packages only supported on devices with EMMC /misc partitions"
781 bcb_dev = {"bcb_dev": fs.device}
782 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
783 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700784if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800785""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800786
787 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
788 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800789 script.WriteRawImage("/recovery", "recovery.img")
790 script.AppendExtra("""
791set_stage("%(bcb_dev)s", "3/3");
792reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700793else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800794""" % bcb_dev)
795
Tao Baod42e97e2016-11-30 12:11:57 -0800796 # Stage 3/3: Make changes.
797 script.Comment("Stage 3/3")
798
Tao Bao6c55a8a2015-04-08 15:30:27 -0700799 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800800 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700801
Doug Zongkere5ff5902012-01-17 10:55:37 -0800802 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700803
Doug Zongker01ce19c2014-02-04 13:48:15 -0800804 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700805
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700806 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800807 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700808 if HasVendorPartition(input_zip):
809 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700810
Doug Zongker4b9596f2014-06-09 14:15:45 -0700811 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800812
Tao Baoe709b092018-02-07 12:40:00 -0800813 # See the notes in WriteBlockIncrementalOTAPackage().
814 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
815
Tao Bao457cbf62017-03-06 09:56:01 -0800816 # Full OTA is done as an "incremental" against an empty source image. This
817 # has the effect of writing new data from the package to the entire
818 # partition, but lets us reuse the updater code that writes incrementals to
819 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800820 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
821 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800822 system_tgt.ResetFileMap()
823 system_diff = common.BlockDifference("system", system_tgt, src=None)
824 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700825
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700826 boot_img = common.GetBootableImage(
827 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800828
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700829 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700830 script.ShowProgress(0.1, 0)
831
Tao Baoe709b092018-02-07 12:40:00 -0800832 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
833 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800834 vendor_tgt.ResetFileMap()
835 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
836 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700837
Tao Bao481bab82017-12-21 11:23:09 -0800838 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700839
Tao Bao481bab82017-12-21 11:23:09 -0800840 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700841 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700842
Doug Zongker01ce19c2014-02-04 13:48:15 -0800843 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700844 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700845
Doug Zongker01ce19c2014-02-04 13:48:15 -0800846 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700847 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700848
Doug Zongker1c390a22009-05-14 19:06:36 -0700849 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700850 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700851
Doug Zongker14833602010-02-02 13:12:04 -0800852 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800853
Doug Zongker922206e2014-03-04 13:16:24 -0800854 if OPTIONS.wipe_user_data:
855 script.ShowProgress(0.1, 10)
856 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700857
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800858 if OPTIONS.two_step:
859 script.AppendExtra("""
860set_stage("%(bcb_dev)s", "");
861""" % bcb_dev)
862 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800863
864 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
865 script.Comment("Stage 1/3")
866 _WriteRecoveryImageToBoot(script, output_zip)
867
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800868 script.AppendExtra("""
869set_stage("%(bcb_dev)s", "2/3");
870reboot_now("%(bcb_dev)s", "");
871endif;
872endif;
873""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800874
Tao Bao5d182562016-02-23 11:38:39 -0800875 script.SetProgress(1)
876 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800877 metadata["ota-required-cache"] = str(script.required_cache)
Tao Baoc0746f42018-02-21 13:17:22 -0800878
879 # We haven't written the metadata entry, which will be done in
880 # FinalizeMetadata.
881 common.ZipClose(output_zip)
882
883 needed_property_files = (
884 NonAbOtaPropertyFiles(),
885 )
886 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700887
Doug Zongkerfc44a512014-08-26 13:10:25 -0700888
Doug Zongker2ea21062010-04-28 16:05:21 -0700889def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800890 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
891 common.ZipWriteStr(output_zip, METADATA_NAME, value,
892 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700893
Doug Zongkerfc44a512014-08-26 13:10:25 -0700894
Tao Bao481bab82017-12-21 11:23:09 -0800895def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800896 # Only incremental OTAs are allowed to reach here.
897 assert OPTIONS.incremental_source is not None
898
Tao Bao481bab82017-12-21 11:23:09 -0800899 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
900 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800901 is_downgrade = long(post_timestamp) < long(pre_timestamp)
902
903 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800904 if not is_downgrade:
905 raise RuntimeError("--downgrade specified but no downgrade detected: "
906 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800907 metadata["ota-downgrade"] = "yes"
908 elif OPTIONS.timestamp:
909 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800910 raise RuntimeError("--override_timestamp specified but no timestamp hack "
911 "needed: pre: %s, post: %s" % (pre_timestamp,
912 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800913 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800914 else:
915 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800916 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800917 "pre: %s, post: %s. Need to specify "
918 "--override_timestamp OR --downgrade to allow "
919 "building the incremental." % (pre_timestamp,
920 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800921 metadata["post-timestamp"] = post_timestamp
922
923
Tao Baodf3a48b2018-01-10 16:30:43 -0800924def GetPackageMetadata(target_info, source_info=None):
925 """Generates and returns the metadata dict.
926
927 It generates a dict() that contains the info to be written into an OTA
928 package (META-INF/com/android/metadata). It also handles the detection of
929 downgrade / timestamp override / data wipe based on the global options.
930
931 Args:
932 target_info: The BuildInfo instance that holds the target build info.
933 source_info: The BuildInfo instance that holds the source build info, or
934 None if generating full OTA.
935
936 Returns:
937 A dict to be written into package metadata entry.
938 """
939 assert isinstance(target_info, BuildInfo)
940 assert source_info is None or isinstance(source_info, BuildInfo)
941
942 metadata = {
943 'post-build' : target_info.fingerprint,
944 'post-build-incremental' : target_info.GetBuildProp(
945 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800946 'post-sdk-level' : target_info.GetBuildProp(
947 'ro.build.version.sdk'),
948 'post-security-patch-level' : target_info.GetBuildProp(
949 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800950 }
951
952 if target_info.is_ab:
953 metadata['ota-type'] = 'AB'
954 metadata['ota-required-cache'] = '0'
955 else:
956 metadata['ota-type'] = 'BLOCK'
957
958 if OPTIONS.wipe_user_data:
959 metadata['ota-wipe'] = 'yes'
960
961 is_incremental = source_info is not None
962 if is_incremental:
963 metadata['pre-build'] = source_info.fingerprint
964 metadata['pre-build-incremental'] = source_info.GetBuildProp(
965 'ro.build.version.incremental')
966 metadata['pre-device'] = source_info.device
967 else:
968 metadata['pre-device'] = target_info.device
969
970 # Detect downgrades, or fill in the post-timestamp.
971 if is_incremental:
972 HandleDowngradeMetadata(metadata, target_info, source_info)
973 else:
974 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
975
976 return metadata
977
978
Tao Bao69203522018-03-08 16:09:01 -0800979class PropertyFiles(object):
980 """A class that computes the property-files string for an OTA package.
981
982 A property-files string is a comma-separated string that contains the
983 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
984 can be fetched directly with the package URL along with the offset/size info.
985 These strings can be used for streaming A/B OTAs, or allowing an updater to
986 download package metadata entry directly, without paying the cost of
987 downloading entire package.
Tao Baof5110492018-03-02 09:47:43 -0800988
Tao Baoae5e4c32018-03-01 19:30:00 -0800989 Computing the final property-files string requires two passes. Because doing
990 the whole package signing (with signapk.jar) will possibly reorder the ZIP
991 entries, which may in turn invalidate earlier computed ZIP entry offset/size
992 values.
993
994 This class provides functions to be called for each pass. The general flow is
995 as follows.
996
Tao Bao69203522018-03-08 16:09:01 -0800997 property_files = PropertyFiles()
Tao Baoae5e4c32018-03-01 19:30:00 -0800998 # The first pass, which writes placeholders before doing initial signing.
999 property_files.Compute()
1000 SignOutput()
1001
1002 # The second pass, by replacing the placeholders with actual data.
1003 property_files.Finalize()
1004 SignOutput()
1005
1006 And the caller can additionally verify the final result.
1007
1008 property_files.Verify()
Tao Baof5110492018-03-02 09:47:43 -08001009 """
1010
Tao Baoae5e4c32018-03-01 19:30:00 -08001011 def __init__(self):
Tao Bao69203522018-03-08 16:09:01 -08001012 self.name = None
1013 self.required = ()
1014 self.optional = ()
Tao Baof5110492018-03-02 09:47:43 -08001015
Tao Baoae5e4c32018-03-01 19:30:00 -08001016 def Compute(self, input_zip):
1017 """Computes and returns a property-files string with placeholders.
Tao Baof5110492018-03-02 09:47:43 -08001018
Tao Baoae5e4c32018-03-01 19:30:00 -08001019 We reserve extra space for the offset and size of the metadata entry itself,
1020 although we don't know the final values until the package gets signed.
Tao Baof5110492018-03-02 09:47:43 -08001021
Tao Baoae5e4c32018-03-01 19:30:00 -08001022 Args:
1023 input_zip: The input ZIP file.
Tao Baof5110492018-03-02 09:47:43 -08001024
Tao Baoae5e4c32018-03-01 19:30:00 -08001025 Returns:
1026 A string with placeholders for the metadata offset/size info, e.g.
1027 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1028 """
1029 return self._GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baof5110492018-03-02 09:47:43 -08001030
Tao Bao3bf8c652018-03-16 12:59:42 -07001031 class InsufficientSpaceException(Exception):
1032 pass
1033
Tao Baoae5e4c32018-03-01 19:30:00 -08001034 def Finalize(self, input_zip, reserved_length):
1035 """Finalizes a property-files string with actual METADATA offset/size info.
1036
1037 The input ZIP file has been signed, with the ZIP entries in the desired
1038 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1039 the ZIP entry offsets and construct the property-files string with actual
1040 data. Note that during this process, we must pad the property-files string
1041 to the reserved length, so that the METADATA entry size remains the same.
1042 Otherwise the entries' offsets and sizes may change again.
1043
1044 Args:
1045 input_zip: The input ZIP file.
1046 reserved_length: The reserved length of the property-files string during
1047 the call to Compute(). The final string must be no more than this
1048 size.
1049
1050 Returns:
1051 A property-files string including the metadata offset/size info, e.g.
1052 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1053
1054 Raises:
Tao Bao3bf8c652018-03-16 12:59:42 -07001055 InsufficientSpaceException: If the reserved length is insufficient to hold
1056 the final string.
Tao Baoae5e4c32018-03-01 19:30:00 -08001057 """
1058 result = self._GetPropertyFilesString(input_zip, reserve_space=False)
Tao Bao3bf8c652018-03-16 12:59:42 -07001059 if len(result) > reserved_length:
1060 raise self.InsufficientSpaceException(
1061 'Insufficient reserved space: reserved={}, actual={}'.format(
1062 reserved_length, len(result)))
1063
Tao Baoae5e4c32018-03-01 19:30:00 -08001064 result += ' ' * (reserved_length - len(result))
1065 return result
1066
1067 def Verify(self, input_zip, expected):
1068 """Verifies the input ZIP file contains the expected property-files string.
1069
1070 Args:
1071 input_zip: The input ZIP file.
1072 expected: The property-files string that's computed from Finalize().
1073
1074 Raises:
1075 AssertionError: On finding a mismatch.
1076 """
1077 actual = self._GetPropertyFilesString(input_zip)
1078 assert actual == expected, \
1079 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1080
1081 def _GetPropertyFilesString(self, zip_file, reserve_space=False):
1082 """Constructs the property-files string per request."""
1083
1084 def ComputeEntryOffsetSize(name):
1085 """Computes the zip entry offset and size."""
1086 info = zip_file.getinfo(name)
1087 offset = info.header_offset + len(info.FileHeader())
1088 size = info.file_size
1089 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1090
1091 tokens = []
Tao Baob6304672018-03-08 16:28:33 -08001092 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baoae5e4c32018-03-01 19:30:00 -08001093 for entry in self.required:
1094 tokens.append(ComputeEntryOffsetSize(entry))
1095 for entry in self.optional:
1096 if entry in zip_file.namelist():
1097 tokens.append(ComputeEntryOffsetSize(entry))
1098
1099 # 'META-INF/com/android/metadata' is required. We don't know its actual
1100 # offset and length (as well as the values for other entries). So we reserve
Tao Bao3bf8c652018-03-16 12:59:42 -07001101 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1102 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1103 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1104 # reserved space serves the metadata entry only.
Tao Baoae5e4c32018-03-01 19:30:00 -08001105 if reserve_space:
Tao Bao3bf8c652018-03-16 12:59:42 -07001106 tokens.append('metadata:' + ' ' * 15)
Tao Baoae5e4c32018-03-01 19:30:00 -08001107 else:
1108 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1109
1110 return ','.join(tokens)
Tao Baof5110492018-03-02 09:47:43 -08001111
Tao Baob6304672018-03-08 16:28:33 -08001112 def _GetPrecomputed(self, input_zip):
1113 """Computes the additional tokens to be included into the property-files.
1114
1115 This applies to tokens without actual ZIP entries, such as
1116 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1117 that they can download the payload metadata directly with the info.
1118
1119 Args:
1120 input_zip: The input zip file.
1121
1122 Returns:
1123 A list of strings (tokens) to be added to the property-files string.
1124 """
1125 # pylint: disable=no-self-use
1126 # pylint: disable=unused-argument
1127 return []
1128
Tao Baof5110492018-03-02 09:47:43 -08001129
Tao Bao69203522018-03-08 16:09:01 -08001130class StreamingPropertyFiles(PropertyFiles):
1131 """A subclass for computing the property-files for streaming A/B OTAs."""
1132
1133 def __init__(self):
1134 super(StreamingPropertyFiles, self).__init__()
1135 self.name = 'ota-streaming-property-files'
1136 self.required = (
1137 # payload.bin and payload_properties.txt must exist.
1138 'payload.bin',
1139 'payload_properties.txt',
1140 )
1141 self.optional = (
1142 # care_map.txt is available only if dm-verity is enabled.
1143 'care_map.txt',
1144 # compatibility.zip is available only if target supports Treble.
1145 'compatibility.zip',
1146 )
1147
1148
Tao Baob6304672018-03-08 16:28:33 -08001149class AbOtaPropertyFiles(StreamingPropertyFiles):
1150 """The property-files for A/B OTA that includes payload_metadata.bin info.
1151
1152 Since P, we expose one more token (aka property-file), in addition to the ones
1153 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1154 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1155 doesn't exist as a separate ZIP entry, but can be used to verify if the
1156 payload can be applied on the given device.
1157
1158 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1159 and the newly added 'ota-property-files' in P. The new token will only be
1160 available in 'ota-property-files'.
1161 """
1162
1163 def __init__(self):
1164 super(AbOtaPropertyFiles, self).__init__()
1165 self.name = 'ota-property-files'
1166
1167 def _GetPrecomputed(self, input_zip):
1168 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1169 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1170
1171 @staticmethod
1172 def _GetPayloadMetadataOffsetAndSize(input_zip):
1173 """Computes the offset and size of the payload metadata for a given package.
1174
1175 (From system/update_engine/update_metadata.proto)
1176 A delta update file contains all the deltas needed to update a system from
1177 one specific version to another specific version. The update format is
1178 represented by this struct pseudocode:
1179
1180 struct delta_update_file {
1181 char magic[4] = "CrAU";
1182 uint64 file_format_version;
1183 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1184
1185 // Only present if format_version > 1:
1186 uint32 metadata_signature_size;
1187
1188 // The Bzip2 compressed DeltaArchiveManifest
1189 char manifest[metadata_signature_size];
1190
1191 // The signature of the metadata (from the beginning of the payload up to
1192 // this location, not including the signature itself). This is a
1193 // serialized Signatures message.
1194 char medatada_signature_message[metadata_signature_size];
1195
1196 // Data blobs for files, no specific format. The specific offset
1197 // and length of each data blob is recorded in the DeltaArchiveManifest.
1198 struct {
1199 char data[];
1200 } blobs[];
1201
1202 // These two are not signed:
1203 uint64 payload_signatures_message_size;
1204 char payload_signatures_message[];
1205 };
1206
1207 'payload-metadata.bin' contains all the bytes from the beginning of the
1208 payload, till the end of 'medatada_signature_message'.
1209 """
1210 payload_info = input_zip.getinfo('payload.bin')
1211 payload_offset = payload_info.header_offset + len(payload_info.FileHeader())
1212 payload_size = payload_info.file_size
1213
1214 with input_zip.open('payload.bin', 'r') as payload_fp:
1215 header_bin = payload_fp.read(24)
1216
1217 # network byte order (big-endian)
1218 header = struct.unpack("!IQQL", header_bin)
1219
1220 # 'CrAU'
1221 magic = header[0]
1222 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1223
1224 manifest_size = header[2]
1225 metadata_signature_size = header[3]
1226 metadata_total = 24 + manifest_size + metadata_signature_size
1227 assert metadata_total < payload_size
1228
1229 return (payload_offset, metadata_total)
1230
1231
Tao Baoc0746f42018-02-21 13:17:22 -08001232class NonAbOtaPropertyFiles(PropertyFiles):
1233 """The property-files for non-A/B OTA.
1234
1235 For non-A/B OTA, the property-files string contains the info for METADATA
1236 entry, with which a system updater can be fetched the package metadata prior
1237 to downloading the entire package.
1238 """
1239
1240 def __init__(self):
1241 super(NonAbOtaPropertyFiles, self).__init__()
1242 self.name = 'ota-property-files'
1243
1244
Tao Bao69203522018-03-08 16:09:01 -08001245def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baof5110492018-03-02 09:47:43 -08001246 """Finalizes the metadata and signs an A/B OTA package.
1247
1248 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1249 that contains the offsets and sizes for the ZIP entries. An example
1250 property-files string is as follows.
1251
1252 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1253
1254 OTA server can pass down this string, in addition to the package URL, to the
1255 system update client. System update client can then fetch individual ZIP
1256 entries (ZIP_STORED) directly at the given offset of the URL.
1257
1258 Args:
1259 metadata: The metadata dict for the package.
1260 input_file: The input ZIP filename that doesn't contain the package METADATA
1261 entry yet.
1262 output_file: The final output ZIP filename.
Tao Bao69203522018-03-08 16:09:01 -08001263 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baof5110492018-03-02 09:47:43 -08001264 """
Tao Baof5110492018-03-02 09:47:43 -08001265
Tao Bao3bf8c652018-03-16 12:59:42 -07001266 def ComputeAllPropertyFiles(input_file, needed_property_files):
1267 # Write the current metadata entry with placeholders.
1268 with zipfile.ZipFile(input_file) as input_zip:
1269 for property_files in needed_property_files:
1270 metadata[property_files.name] = property_files.Compute(input_zip)
1271 namelist = input_zip.namelist()
Tao Baof5110492018-03-02 09:47:43 -08001272
Tao Bao3bf8c652018-03-16 12:59:42 -07001273 if METADATA_NAME in namelist:
1274 common.ZipDelete(input_file, METADATA_NAME)
1275 output_zip = zipfile.ZipFile(input_file, 'a')
1276 WriteMetadata(metadata, output_zip)
1277 common.ZipClose(output_zip)
1278
1279 if OPTIONS.no_signing:
1280 return input_file
1281
Tao Baoc0746f42018-02-21 13:17:22 -08001282 prelim_signing = common.MakeTempFile(suffix='.zip')
1283 SignOutput(input_file, prelim_signing)
Tao Bao3bf8c652018-03-16 12:59:42 -07001284 return prelim_signing
Tao Baof5110492018-03-02 09:47:43 -08001285
Tao Bao3bf8c652018-03-16 12:59:42 -07001286 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1287 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1288 for property_files in needed_property_files:
1289 metadata[property_files.name] = property_files.Finalize(
1290 prelim_signing_zip, len(metadata[property_files.name]))
1291
1292 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1293 # entries, as well as padding the entry headers. We do a preliminary signing
1294 # (with an incomplete metadata entry) to allow that to happen. Then compute
1295 # the ZIP entry offsets, write back the final metadata and do the final
1296 # signing.
1297 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1298 try:
1299 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1300 except PropertyFiles.InsufficientSpaceException:
1301 # Even with the preliminary signing, the entry orders may change
1302 # dramatically, which leads to insufficiently reserved space during the
1303 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1304 # preliminary signing works, based on the already ordered ZIP entries, to
1305 # address the issue.
1306 prelim_signing = ComputeAllPropertyFiles(
1307 prelim_signing, needed_property_files)
1308 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baof5110492018-03-02 09:47:43 -08001309
1310 # Replace the METADATA entry.
1311 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Bao3bf8c652018-03-16 12:59:42 -07001312 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baof5110492018-03-02 09:47:43 -08001313 WriteMetadata(metadata, output_zip)
1314 common.ZipClose(output_zip)
1315
1316 # Re-sign the package after updating the metadata entry.
Tao Baoc0746f42018-02-21 13:17:22 -08001317 if OPTIONS.no_signing:
1318 output_file = prelim_signing
1319 else:
1320 SignOutput(prelim_signing, output_file)
Tao Baof5110492018-03-02 09:47:43 -08001321
1322 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao3bf8c652018-03-16 12:59:42 -07001323 with zipfile.ZipFile(output_file) as output_zip:
Tao Bao69203522018-03-08 16:09:01 -08001324 for property_files in needed_property_files:
1325 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baof5110492018-03-02 09:47:43 -08001326
1327
Tao Baoc0746f42018-02-21 13:17:22 -08001328def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001329 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1330 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001331
Tao Bao481bab82017-12-21 11:23:09 -08001332 target_api_version = target_info["recovery_api_version"]
1333 source_api_version = source_info["recovery_api_version"]
1334 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001335 print("WARNING: generating edify script for a source that "
1336 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001337
Tao Bao481bab82017-12-21 11:23:09 -08001338 script = edify_generator.EdifyGenerator(
1339 source_api_version, target_info, fstab=source_info["fstab"])
1340
1341 if target_info.oem_props or source_info.oem_props:
1342 if not OPTIONS.oem_no_mount:
1343 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001344
Tao Baodf3a48b2018-01-10 16:30:43 -08001345 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001346
Tao Baoc0746f42018-02-21 13:17:22 -08001347 if not OPTIONS.no_signing:
1348 staging_file = common.MakeTempFile(suffix='.zip')
1349 else:
1350 staging_file = output_file
1351
1352 output_zip = zipfile.ZipFile(
1353 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1354
Geremy Condra36bd3652014-02-06 19:45:10 -08001355 device_specific = common.DeviceSpecificParams(
1356 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001357 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001358 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001359 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001360 output_zip=output_zip,
1361 script=script,
1362 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001363 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001364
Geremy Condra36bd3652014-02-06 19:45:10 -08001365 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001366 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001367 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001368 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001369 updating_boot = (not OPTIONS.two_step and
1370 (source_boot.data != target_boot.data))
1371
Geremy Condra36bd3652014-02-06 19:45:10 -08001372 target_recovery = common.GetBootableImage(
1373 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001374
Tao Baoe709b092018-02-07 12:40:00 -08001375 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1376 # shared blocks (i.e. some blocks will show up in multiple files' block
1377 # list). We can only allocate such shared blocks to the first "owner", and
1378 # disable imgdiff for all later occurrences.
1379 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1380 target_info.get('ext4_share_dup_blocks') == "true")
1381 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1382 allow_shared_blocks)
1383 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
1384 allow_shared_blocks)
Tao Baodd2a5892015-03-12 12:32:37 -07001385
Tao Bao0582cb62017-12-21 11:47:01 -08001386 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001387 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001388 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001389
Tao Baof8acad12016-07-07 09:09:58 -07001390 # Check the first block of the source system partition for remount R/W only
1391 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001392 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001393 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001394 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1395 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1396 # b) the blocks listed in block map may not contain all the bytes for a given
1397 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001398 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001399 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1400 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001401 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001402 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001403 version=blockimgdiff_version,
1404 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001405
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001406 if HasVendorPartition(target_zip):
1407 if not HasVendorPartition(source_zip):
1408 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001409 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1410 allow_shared_blocks)
1411 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip,
1412 allow_shared_blocks)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001413
1414 # Check first block of vendor partition for remount R/W only if
1415 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001416 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001417 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001418 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001419 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001420 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001421 version=blockimgdiff_version,
1422 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001423 else:
1424 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001425
Tao Baobcd1d162017-08-26 13:10:26 -07001426 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001427 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001428
Tao Bao481bab82017-12-21 11:23:09 -08001429 # Assertions (e.g. device properties check).
1430 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001431 device_specific.IncrementalOTA_Assertions()
1432
1433 # Two-step incremental package strategy (in chronological order,
1434 # which is *not* the order in which the generated script has
1435 # things):
1436 #
1437 # if stage is not "2/3" or "3/3":
1438 # do verification on current system
1439 # write recovery image to boot partition
1440 # set stage to "2/3"
1441 # reboot to boot partition and restart recovery
1442 # else if stage is "2/3":
1443 # write recovery image to recovery partition
1444 # set stage to "3/3"
1445 # reboot to recovery partition and restart recovery
1446 # else:
1447 # (stage must be "3/3")
1448 # perform update:
1449 # patch system files, etc.
1450 # force full install of new boot image
1451 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001452 # complete script normally
1453 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001454
1455 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001456 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001457 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001458 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001459 assert fs.fs_type.upper() == "EMMC", \
1460 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001461 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001462 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1463 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001464if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001465""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001466
1467 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1468 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001469 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001470 script.WriteRawImage("/recovery", "recovery.img")
1471 script.AppendExtra("""
1472set_stage("%(bcb_dev)s", "3/3");
1473reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001474else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001475""" % bcb_dev)
1476
Tao Baod42e97e2016-11-30 12:11:57 -08001477 # Stage 1/3: (a) Verify the current system.
1478 script.Comment("Stage 1/3")
1479
Tao Bao6c55a8a2015-04-08 15:30:27 -07001480 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001481 script.Print("Source: {}".format(source_info.fingerprint))
1482 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001483
Geremy Condra36bd3652014-02-06 19:45:10 -08001484 script.Print("Verifying current system...")
1485
1486 device_specific.IncrementalOTA_VerifyBegin()
1487
Tao Bao481bab82017-12-21 11:23:09 -08001488 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001489
Tao Baod8d14be2016-02-04 14:26:02 -08001490 # Check the required cache size (i.e. stashed blocks).
1491 size = []
1492 if system_diff:
1493 size.append(system_diff.required_cache)
1494 if vendor_diff:
1495 size.append(vendor_diff.required_cache)
1496
Geremy Condra36bd3652014-02-06 19:45:10 -08001497 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001498 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001499 d = common.Difference(target_boot, source_boot)
1500 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001501 if d is None:
1502 include_full_boot = True
1503 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1504 else:
1505 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001506
Tao Bao89fbb0f2017-01-10 10:47:58 -08001507 print("boot target: %d source: %d diff: %d" % (
1508 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001509
Doug Zongkerf8340082014-08-05 10:39:37 -07001510 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001511
Doug Zongkerf8340082014-08-05 10:39:37 -07001512 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1513 (boot_type, boot_device,
1514 source_boot.size, source_boot.sha1,
1515 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001516 size.append(target_boot.size)
1517
1518 if size:
1519 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001520
1521 device_specific.IncrementalOTA_VerifyEnd()
1522
1523 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001524 # Stage 1/3: (b) Write recovery image to /boot.
1525 _WriteRecoveryImageToBoot(script, output_zip)
1526
Geremy Condra36bd3652014-02-06 19:45:10 -08001527 script.AppendExtra("""
1528set_stage("%(bcb_dev)s", "2/3");
1529reboot_now("%(bcb_dev)s", "");
1530else
1531""" % bcb_dev)
1532
Tao Baod42e97e2016-11-30 12:11:57 -08001533 # Stage 3/3: Make changes.
1534 script.Comment("Stage 3/3")
1535
Jesse Zhao75bcea02015-01-06 10:59:53 -08001536 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001537 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001538 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001539 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001540
Geremy Condra36bd3652014-02-06 19:45:10 -08001541 script.Comment("---- start making changes here ----")
1542
1543 device_specific.IncrementalOTA_InstallBegin()
1544
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001545 system_diff.WriteScript(script, output_zip,
1546 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001547
Doug Zongkerfc44a512014-08-26 13:10:25 -07001548 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001549 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001550
1551 if OPTIONS.two_step:
1552 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1553 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001554 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001555
1556 if not OPTIONS.two_step:
1557 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001558 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001559 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001560 script.Print("Installing boot image...")
1561 script.WriteRawImage("/boot", "boot.img")
1562 else:
1563 # Produce the boot image by applying a patch to the current
1564 # contents of the boot partition, and write it back to the
1565 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001566 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001567 script.Print("Patching boot image...")
1568 script.ShowProgress(0.1, 10)
1569 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1570 % (boot_type, boot_device,
1571 source_boot.size, source_boot.sha1,
1572 target_boot.size, target_boot.sha1),
1573 "-",
1574 target_boot.size, target_boot.sha1,
1575 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001576 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001577 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001578
1579 # Do device-specific installation (eg, write radio image).
1580 device_specific.IncrementalOTA_InstallEnd()
1581
1582 if OPTIONS.extra_script is not None:
1583 script.AppendExtra(OPTIONS.extra_script)
1584
Doug Zongker922206e2014-03-04 13:16:24 -08001585 if OPTIONS.wipe_user_data:
1586 script.Print("Erasing user data...")
1587 script.FormatPartition("/data")
1588
Geremy Condra36bd3652014-02-06 19:45:10 -08001589 if OPTIONS.two_step:
1590 script.AppendExtra("""
1591set_stage("%(bcb_dev)s", "");
1592endif;
1593endif;
1594""" % bcb_dev)
1595
1596 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001597 # For downgrade OTAs, we prefer to use the update-binary in the source
1598 # build that is actually newer than the one in the target build.
1599 if OPTIONS.downgrade:
1600 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1601 else:
1602 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001603 metadata["ota-required-cache"] = str(script.required_cache)
Tao Baoc0746f42018-02-21 13:17:22 -08001604
1605 # We haven't written the metadata entry yet, which will be handled in
1606 # FinalizeMetadata().
1607 common.ZipClose(output_zip)
1608
1609 # Sign the generated zip package unless no_signing is specified.
1610 needed_property_files = (
1611 NonAbOtaPropertyFiles(),
1612 )
1613 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001614
Doug Zongker32b527d2014-03-04 10:03:02 -08001615
Tao Bao15a146a2018-02-21 16:06:59 -08001616def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001617 """Returns a target-files.zip file for generating secondary payload.
1618
1619 Although the original target-files.zip already contains secondary slot
1620 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1621 ones without _other suffix. Note that we cannot instead modify the names in
1622 META/ab_partitions.txt, because there are no matching partitions on device.
1623
1624 For the partitions that don't have secondary images, the ones for primary
1625 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1626 bootloader images in the inactive slot.
1627
1628 Args:
1629 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001630 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001631
1632 Returns:
1633 The filename of the target-files.zip for generating secondary payload.
1634 """
1635 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1636 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1637
Tao Baodba59ee2018-01-09 13:21:02 -08001638 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
1639 with zipfile.ZipFile(input_file, 'r') as input_zip:
1640 infolist = input_zip.infolist()
1641
1642 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001643 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1644 if info.filename == 'IMAGES/system_other.img':
1645 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1646
1647 # Primary images and friends need to be skipped explicitly.
1648 elif info.filename in ('IMAGES/system.img',
1649 'IMAGES/system.map'):
1650 pass
1651
Tao Bao15a146a2018-02-21 16:06:59 -08001652 # Skip copying the postinstall config if requested.
1653 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1654 pass
1655
Tao Baof7140c02018-01-30 17:09:24 -08001656 elif info.filename.startswith(('META/', 'IMAGES/')):
1657 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1658
Tao Baof7140c02018-01-30 17:09:24 -08001659 common.ZipClose(target_zip)
1660
1661 return target_file
1662
1663
Tao Bao15a146a2018-02-21 16:06:59 -08001664def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1665 """Returns a target-files.zip that's not containing postinstall_config.txt.
1666
1667 This allows brillo_update_payload script to skip writing all the postinstall
1668 hooks in the generated payload. The input target-files.zip file will be
1669 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1670 contain the postinstall_config.txt entry, the input file will be returned.
1671
1672 Args:
1673 input_file: The input target-files.zip filename.
1674
1675 Returns:
1676 The filename of target-files.zip that doesn't contain postinstall config.
1677 """
1678 # We should only make a copy if postinstall_config entry exists.
1679 with zipfile.ZipFile(input_file, 'r') as input_zip:
1680 if POSTINSTALL_CONFIG not in input_zip.namelist():
1681 return input_file
1682
1683 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1684 shutil.copyfile(input_file, target_file)
1685 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1686 return target_file
1687
1688
Tao Baoc098e9e2016-01-07 13:03:56 -08001689def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1690 source_file=None):
Tao Baof5110492018-03-02 09:47:43 -08001691 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001692 # Stage the output zip package for package signing.
Tao Baoc0746f42018-02-21 13:17:22 -08001693 if not OPTIONS.no_signing:
1694 staging_file = common.MakeTempFile(suffix='.zip')
1695 else:
1696 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001697 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001698 compression=zipfile.ZIP_DEFLATED)
1699
Tao Bao481bab82017-12-21 11:23:09 -08001700 if source_file is not None:
1701 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1702 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1703 else:
1704 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1705 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001706
Tao Bao481bab82017-12-21 11:23:09 -08001707 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001708 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001709
Tao Bao15a146a2018-02-21 16:06:59 -08001710 if OPTIONS.skip_postinstall:
1711 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1712
Tao Baoc7b403a2018-01-30 18:19:04 -08001713 # Generate payload.
1714 payload = Payload()
1715 payload.Generate(target_file, source_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001716
Tao Baoc7b403a2018-01-30 18:19:04 -08001717 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001718 payload_signer = PayloadSigner()
1719 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001720
Tao Baoc7b403a2018-01-30 18:19:04 -08001721 # Write the payload into output zip.
1722 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001723
Tao Baof7140c02018-01-30 17:09:24 -08001724 # Generate and include the secondary payload that installs secondary images
1725 # (e.g. system_other.img).
1726 if OPTIONS.include_secondary:
1727 # We always include a full payload for the secondary slot, even when
1728 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001729 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1730 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001731 secondary_payload = Payload(secondary=True)
Tao Baof7140c02018-01-30 17:09:24 -08001732 secondary_payload.Generate(secondary_target_file)
1733 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001734 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001735
Tianjie Xucfa86222016-03-07 16:31:19 -08001736 # If dm-verity is supported for the device, copy contents of care_map
1737 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001738 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001739 if (target_info.get("verity") == "true" or
1740 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001741 care_map_path = "META/care_map.txt"
1742 namelist = target_zip.namelist()
1743 if care_map_path in namelist:
1744 care_map_data = target_zip.read(care_map_path)
Tao Baoc7b403a2018-01-30 18:19:04 -08001745 # In order to support streaming, care_map.txt needs to be packed as
1746 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001747 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001748 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001749 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001750 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001751
Tao Baobcd1d162017-08-26 13:10:26 -07001752 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001753 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001754
Tao Bao21803d32017-04-19 10:16:09 -07001755 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001756
Tao Baof5110492018-03-02 09:47:43 -08001757 # We haven't written the metadata entry yet, which will be handled in
1758 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001759 common.ZipClose(output_zip)
1760
Tao Baob6304672018-03-08 16:28:33 -08001761 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1762 # all the info of the latter. However, system updaters and OTA servers need to
1763 # take time to switch to the new flag. We keep both of the flags for
1764 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Bao69203522018-03-08 16:09:01 -08001765 needed_property_files = (
Tao Baob6304672018-03-08 16:28:33 -08001766 AbOtaPropertyFiles(),
Tao Bao69203522018-03-08 16:09:01 -08001767 StreamingPropertyFiles(),
1768 )
1769 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001770
Tao Baoc098e9e2016-01-07 13:03:56 -08001771
Doug Zongkereef39442009-04-02 12:14:19 -07001772def main(argv):
1773
1774 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001775 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001776 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001777 elif o in ("-i", "--incremental_from"):
1778 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001779 elif o == "--full_radio":
1780 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001781 elif o == "--full_bootloader":
1782 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001783 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001784 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001785 elif o == "--downgrade":
1786 OPTIONS.downgrade = True
1787 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001788 elif o == "--override_timestamp":
1789 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001790 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001791 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001792 elif o == "--oem_no_mount":
1793 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001794 elif o in ("-e", "--extra_script"):
1795 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001796 elif o in ("-t", "--worker_threads"):
1797 if a.isdigit():
1798 OPTIONS.worker_threads = int(a)
1799 else:
1800 raise ValueError("Cannot parse value %r for option %r - only "
1801 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001802 elif o in ("-2", "--two_step"):
1803 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001804 elif o == "--include_secondary":
1805 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001806 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001807 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001808 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001809 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001810 elif o == "--block":
1811 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001812 elif o in ("-b", "--binary"):
1813 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001814 elif o == "--stash_threshold":
1815 try:
1816 OPTIONS.stash_threshold = float(a)
1817 except ValueError:
1818 raise ValueError("Cannot parse value %r for option %r - expecting "
1819 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001820 elif o == "--log_diff":
1821 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001822 elif o == "--payload_signer":
1823 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001824 elif o == "--payload_signer_args":
1825 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001826 elif o == "--extracted_input_target_files":
1827 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001828 elif o == "--skip_postinstall":
1829 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001830 else:
1831 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001832 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001833
1834 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001835 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001836 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001837 "package_key=",
1838 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001839 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001840 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001841 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001842 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001843 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001844 "extra_script=",
1845 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001846 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001847 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001848 "no_signing",
1849 "block",
1850 "binary=",
1851 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001852 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001853 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001854 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001855 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001856 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001857 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001858 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001859 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001860 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001861
1862 if len(args) != 2:
1863 common.Usage(__doc__)
1864 sys.exit(1)
1865
Tao Bao5d182562016-02-23 11:38:39 -08001866 if OPTIONS.downgrade:
1867 # Sanity check to enforce a data wipe.
1868 if not OPTIONS.wipe_user_data:
1869 raise ValueError("Cannot downgrade without a data wipe")
1870
1871 # We should only allow downgrading incrementals (as opposed to full).
1872 # Otherwise the device may go back from arbitrary build with this full
1873 # OTA package.
1874 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001875 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001876
Tao Bao3e6161a2017-02-28 11:48:48 -08001877 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1878 "Cannot have --downgrade AND --override_timestamp both"
1879
Tao Bao2db13852018-01-08 22:28:57 -08001880 # Load the build info dicts from the zip directly or the extracted input
1881 # directory. We don't need to unzip the entire target-files zips, because they
1882 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1883 # When loading the info dicts, we don't need to provide the second parameter
1884 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1885 # some properties with their actual paths, such as 'selinux_fc',
1886 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001887 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001888 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001889 else:
Tao Bao2db13852018-01-08 22:28:57 -08001890 with zipfile.ZipFile(args[0], 'r') as input_zip:
1891 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001892
Tao Bao2db13852018-01-08 22:28:57 -08001893 if OPTIONS.verbose:
1894 print("--- target info ---")
1895 common.DumpInfoDict(OPTIONS.info_dict)
1896
1897 # Load the source build dict if applicable.
1898 if OPTIONS.incremental_source is not None:
1899 OPTIONS.target_info_dict = OPTIONS.info_dict
1900 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1901 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1902
1903 if OPTIONS.verbose:
1904 print("--- source info ---")
1905 common.DumpInfoDict(OPTIONS.source_info_dict)
1906
1907 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001908 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1909
Tao Baoc098e9e2016-01-07 13:03:56 -08001910 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1911
Christian Oderf63e2cd2017-05-01 22:30:15 +02001912 # Use the default key to sign the package if not specified with package_key.
1913 # package_keys are needed on ab_updates, so always define them if an
1914 # ab_update is getting created.
1915 if not OPTIONS.no_signing or ab_update:
1916 if OPTIONS.package_key is None:
1917 OPTIONS.package_key = OPTIONS.info_dict.get(
1918 "default_system_dev_certificate",
1919 "build/target/product/security/testkey")
1920 # Get signing keys
1921 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1922
Tao Baoc098e9e2016-01-07 13:03:56 -08001923 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001924 WriteABOTAPackageWithBrilloScript(
1925 target_file=args[0],
1926 output_file=args[1],
1927 source_file=OPTIONS.incremental_source)
1928
Tao Bao89fbb0f2017-01-10 10:47:58 -08001929 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001930 return
1931
Tao Bao2db13852018-01-08 22:28:57 -08001932 # Sanity check the loaded info dicts first.
1933 if OPTIONS.info_dict.get("no_recovery") == "true":
1934 raise common.ExternalError(
1935 "--- target build has specified no recovery ---")
1936
1937 # Non-A/B OTAs rely on /cache partition to store temporary files.
1938 cache_size = OPTIONS.info_dict.get("cache_size")
1939 if cache_size is None:
1940 print("--- can't determine the cache partition size ---")
1941 OPTIONS.cache_size = cache_size
1942
Doug Zongker1c390a22009-05-14 19:06:36 -07001943 if OPTIONS.extra_script is not None:
1944 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1945
Dan Willemsencea5cd22017-03-21 14:44:27 -07001946 if OPTIONS.extracted_input is not None:
1947 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001948 else:
1949 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001950 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001951 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001952
Tao Bao2db13852018-01-08 22:28:57 -08001953 # If the caller explicitly specified the device-specific extensions path via
1954 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1955 # is present in the target target_files. Otherwise, take the path of the file
1956 # from 'tool_extensions' in the info dict and look for that in the local
1957 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001958 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001959 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1960 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001961 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001962 OPTIONS.device_specific = from_input
1963 else:
Tao Bao2db13852018-01-08 22:28:57 -08001964 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001965
Doug Zongker37974732010-09-16 17:44:38 -07001966 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001967 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001968
Tao Bao767e3ac2015-11-10 12:19:19 -08001969 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001970 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08001971 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Baoc0746f42018-02-21 13:17:22 -08001972 WriteFullOTAPackage(
1973 input_zip,
1974 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08001975
Tao Bao32b80dc2018-01-08 22:50:47 -08001976 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001977 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001978 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001979 OPTIONS.source_tmp = common.UnzipTemp(
1980 OPTIONS.incremental_source, UNZIP_PATTERN)
1981 with zipfile.ZipFile(args[0], 'r') as input_zip, \
1982 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Baoc0746f42018-02-21 13:17:22 -08001983 WriteBlockIncrementalOTAPackage(
1984 input_zip,
1985 source_zip,
1986 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08001987
1988 if OPTIONS.log_diff:
1989 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001990 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001991 target_files_diff.recursiveDiff(
1992 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001993
Tao Bao89fbb0f2017-01-10 10:47:58 -08001994 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001995
1996
1997if __name__ == '__main__':
1998 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001999 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002000 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002001 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002002 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002003 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002004 finally:
2005 common.Cleanup()