blob: 968fd773b8e08dd85cad6608bcd018e93981bb37 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070027 to an older one (e.g. downgrading from P preview back to O MR1).
28 "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29 will always be enforced when using this flag, so "ota-wipe=yes" will also
30 be included in the metadata file. The update-binary in the source build
31 will be used in the OTA package, unless --binary flag is specified. Please
32 also check the comment for --override_timestamp below.
Tao Bao30df8b42018-04-23 15:32:53 -070033
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
41 META/misc_info.txt, or "build/target/product/security/testkey" if that
42 value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070049 to an older one (based on timestamp comparison), by setting the downgrade
50 flag in the package metadata. This differs from --downgrade flag, as we
51 don't enforce a data wipe with this flag. Because we know for sure this is
52 NOT an actual downgrade case, but two builds happen to be cut in a reverse
53 order (e.g. from two branches). A legit use case is that we cut a new
54 build C (after having A and B), but want to enfore an update path of A ->
55 C -> B. Specifying --downgrade may not help since that would enforce a
56 data wipe for C -> B update.
57
58 We used to set a fake timestamp in the package metadata for this flow. But
59 now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60 based on timestamp) with the same "ota-downgrade=yes" flag, with the
61 difference being whether "ota-wipe=yes" is set.
Doug Zongkereef39442009-04-02 12:14:19 -070062
Tao Bao30df8b42018-04-23 15:32:53 -070063 --wipe_user_data
64 Generate an OTA package that will wipe the user data partition when
65 installed.
66
67Non-A/B OTA specific options
68
69 -b (--binary) <file>
70 Use the given binary as the update-binary in the output package, instead
71 of the binary in the build's target_files. Use for development only.
72
73 --block
74 Generate a block-based OTA for non-A/B device. We have deprecated the
75 support for file-based OTA since O. Block-based OTA will be used by
76 default for all non-A/B devices. Keeping this flag here to not break
77 existing callers.
78
79 -e (--extra_script) <file>
80 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070081
leozwangaa6c1a12015-08-14 10:57:58 -070082 --full_bootloader
83 Similar to --full_radio. When generating an incremental OTA, always
84 include a full copy of bootloader image.
85
Tao Bao30df8b42018-04-23 15:32:53 -070086 --full_radio
87 When generating an incremental OTA, always include a full copy of radio
88 image. This option is only meaningful when -i is specified, because a full
89 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -070090
Tao Bao30df8b42018-04-23 15:32:53 -070091 --log_diff <file>
92 Generate a log file that shows the differences in the source and target
93 builds for an incremental package. This option is only meaningful when -i
94 is specified.
95
96 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080097 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -080098 properties on the OEM partition of the intended device. Multiple expected
99 values can be used by providing multiple files. Only the first dict will
100 be used to compute fingerprint, while the rest will be used to assert
101 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800102
Tao Bao8608cde2016-02-25 19:49:55 -0800103 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700104 For devices with OEM-specific properties but without an OEM partition, do
105 not mount the OEM partition in the updater-script. This should be very
106 rarely used, since it's expected to have a dedicated OEM partition for
107 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800108
Tao Bao30df8b42018-04-23 15:32:53 -0700109 --stash_threshold <float>
110 Specify the threshold that will be used to compute the maximum allowed
111 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700112
Tao Bao30df8b42018-04-23 15:32:53 -0700113 -t (--worker_threads) <int>
114 Specify the number of worker-threads that will be used when generating
115 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800116
Tao Bao30df8b42018-04-23 15:32:53 -0700117 --verify
118 Verify the checksums of the updated system and vendor (if any) partitions.
119 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700120
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800121 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700122 Generate a 'two-step' OTA package, where recovery is updated first, so
123 that any changes made to the system partition are done using the new
124 recovery (new kernel, etc.).
125
126A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800127
Tao Baof7140c02018-01-30 17:09:24 -0800128 --include_secondary
129 Additionally include the payload for secondary slot images (default:
130 False). Only meaningful when generating A/B OTAs.
131
132 By default, an A/B OTA package doesn't contain the images for the
133 secondary slot (e.g. system_other.img). Specifying this flag allows
134 generating a separate payload that will install secondary slot images.
135
136 Such a package needs to be applied in a two-stage manner, with a reboot
137 in-between. During the first stage, the updater applies the primary
138 payload only. Upon finishing, it reboots the device into the newly updated
139 slot. It then continues to install the secondary payload to the inactive
140 slot, but without switching the active slot at the end (needs the matching
141 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
142
143 Due to the special install procedure, the secondary payload will be always
144 generated as a full payload.
145
Tao Baodea0f8b2016-06-20 17:55:06 -0700146 --payload_signer <signer>
147 Specify the signer when signing the payload and metadata for A/B OTAs.
148 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
149 with the package private key. If the private key cannot be accessed
150 directly, a payload signer that knows how to do that should be specified.
151 The signer will be supplied with "-inkey <path_to_key>",
152 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700153
154 --payload_signer_args <args>
155 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800156
157 --skip_postinstall
158 Skip the postinstall hooks when generating an A/B OTA package (default:
159 False). Note that this discards ALL the hooks, including non-optional
160 ones. Should only be used if caller knows it's safe to do so (e.g. all the
161 postinstall work is to dexopt apps and a data wipe will happen immediately
162 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700163"""
164
Tao Bao89fbb0f2017-01-10 10:47:58 -0800165from __future__ import print_function
166
Doug Zongkerfc44a512014-08-26 13:10:25 -0700167import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800168import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700169import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800170import shutil
Tao Baob6304672018-03-08 16:28:33 -0800171import struct
Tao Bao481bab82017-12-21 11:23:09 -0800172import subprocess
173import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700174import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700175import zipfile
176
177import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700178import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -0700179
Tao Bao481bab82017-12-21 11:23:09 -0800180if sys.hexversion < 0x02070000:
181 print("Python 2.7 or newer is required.", file=sys.stderr)
182 sys.exit(1)
183
184
Doug Zongkereef39442009-04-02 12:14:19 -0700185OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700186OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700187OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700188OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700189OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700190OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800191OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700192OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700193OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
194if OPTIONS.worker_threads == 0:
195 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800196OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800197OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900198OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800199OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800200OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700201OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800202OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700203OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700204OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700205# Stash size cannot exceed cache_size * threshold.
206OPTIONS.cache_size = None
207OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800208OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700209OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700210OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700211OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200212OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800213OPTIONS.skip_postinstall = False
214
Tao Bao8dcf7382015-05-21 14:09:49 -0700215
Tao Bao2dd1c482017-02-03 16:49:39 -0800216METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800217POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800218UNZIP_PATTERN = ['IMAGES/*', 'META/*']
219
Tao Bao2dd1c482017-02-03 16:49:39 -0800220
Tao Bao481bab82017-12-21 11:23:09 -0800221class BuildInfo(object):
222 """A class that holds the information for a given build.
223
224 This class wraps up the property querying for a given source or target build.
225 It abstracts away the logic of handling OEM-specific properties, and caches
226 the commonly used properties such as fingerprint.
227
228 There are two types of info dicts: a) build-time info dict, which is generated
229 at build time (i.e. included in a target_files zip); b) OEM info dict that is
230 specified at package generation time (via command line argument
231 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
232 having "oem_fingerprint_properties" in build-time info dict), all the queries
233 would be answered based on build-time info dict only. Otherwise if using
234 OEM-specific properties, some of them will be calculated from two info dicts.
235
236 Users can query properties similarly as using a dict() (e.g. info['fstab']),
237 or to query build properties via GetBuildProp() or GetVendorBuildProp().
238
239 Attributes:
240 info_dict: The build-time info dict.
241 is_ab: Whether it's a build that uses A/B OTA.
242 oem_dicts: A list of OEM dicts.
243 oem_props: A list of OEM properties that should be read from OEM dicts; None
244 if the build doesn't use any OEM-specific property.
245 fingerprint: The fingerprint of the build, which would be calculated based
246 on OEM properties if applicable.
247 device: The device name, which could come from OEM dicts if applicable.
248 """
249
250 def __init__(self, info_dict, oem_dicts):
251 """Initializes a BuildInfo instance with the given dicts.
252
253 Arguments:
254 info_dict: The build-time info dict.
255 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
256 that it always uses the first dict to calculate the fingerprint or the
257 device name. The rest would be used for asserting OEM properties only
258 (e.g. one package can be installed on one of these devices).
259 """
260 self.info_dict = info_dict
261 self.oem_dicts = oem_dicts
262
263 self._is_ab = info_dict.get("ab_update") == "true"
264 self._oem_props = info_dict.get("oem_fingerprint_properties")
265
266 if self._oem_props:
267 assert oem_dicts, "OEM source required for this build"
268
269 # These two should be computed only after setting self._oem_props.
270 self._device = self.GetOemProperty("ro.product.device")
271 self._fingerprint = self.CalculateFingerprint()
272
273 @property
274 def is_ab(self):
275 return self._is_ab
276
277 @property
278 def device(self):
279 return self._device
280
281 @property
282 def fingerprint(self):
283 return self._fingerprint
284
285 @property
286 def oem_props(self):
287 return self._oem_props
288
289 def __getitem__(self, key):
290 return self.info_dict[key]
291
292 def get(self, key, default=None):
293 return self.info_dict.get(key, default)
294
295 def GetBuildProp(self, prop):
296 """Returns the inquired build property."""
297 try:
298 return self.info_dict.get("build.prop", {})[prop]
299 except KeyError:
300 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
301
302 def GetVendorBuildProp(self, prop):
303 """Returns the inquired vendor build property."""
304 try:
305 return self.info_dict.get("vendor.build.prop", {})[prop]
306 except KeyError:
307 raise common.ExternalError(
308 "couldn't find %s in vendor.build.prop" % (prop,))
309
310 def GetOemProperty(self, key):
311 if self.oem_props is not None and key in self.oem_props:
312 return self.oem_dicts[0][key]
313 return self.GetBuildProp(key)
314
315 def CalculateFingerprint(self):
316 if self.oem_props is None:
317 return self.GetBuildProp("ro.build.fingerprint")
318 return "%s/%s/%s:%s" % (
319 self.GetOemProperty("ro.product.brand"),
320 self.GetOemProperty("ro.product.name"),
321 self.GetOemProperty("ro.product.device"),
322 self.GetBuildProp("ro.build.thumbprint"))
323
324 def WriteMountOemScript(self, script):
325 assert self.oem_props is not None
326 recovery_mount_options = self.info_dict.get("recovery_mount_options")
327 script.Mount("/oem", recovery_mount_options)
328
329 def WriteDeviceAssertions(self, script, oem_no_mount):
330 # Read the property directly if not using OEM properties.
331 if not self.oem_props:
332 script.AssertDevice(self.device)
333 return
334
335 # Otherwise assert OEM properties.
336 if not self.oem_dicts:
337 raise common.ExternalError(
338 "No OEM file provided to answer expected assertions")
339
340 for prop in self.oem_props.split():
341 values = []
342 for oem_dict in self.oem_dicts:
343 if prop in oem_dict:
344 values.append(oem_dict[prop])
345 if not values:
346 raise common.ExternalError(
347 "The OEM file is missing the property %s" % (prop,))
348 script.AssertOemProperty(prop, values, oem_no_mount)
349
350
Tao Baofabe0832018-01-17 15:52:28 -0800351class PayloadSigner(object):
352 """A class that wraps the payload signing works.
353
354 When generating a Payload, hashes of the payload and metadata files will be
355 signed with the device key, either by calling an external payload signer or
356 by calling openssl with the package key. This class provides a unified
357 interface, so that callers can just call PayloadSigner.Sign().
358
359 If an external payload signer has been specified (OPTIONS.payload_signer), it
360 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
361 that the signing key should be provided as part of the payload_signer_args.
362 Otherwise without an external signer, it uses the package key
363 (OPTIONS.package_key) and calls openssl for the signing works.
364 """
365
366 def __init__(self):
367 if OPTIONS.payload_signer is None:
368 # Prepare the payload signing key.
369 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
370 pw = OPTIONS.key_passwords[OPTIONS.package_key]
371
372 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
373 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
374 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
375 cmd.extend(["-out", signing_key])
376
377 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
378 stderr=subprocess.STDOUT)
379 stdoutdata, _ = get_signing_key.communicate()
380 assert get_signing_key.returncode == 0, \
381 "Failed to get signing key: {}".format(stdoutdata)
382
383 self.signer = "openssl"
384 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
385 "-pkeyopt", "digest:sha256"]
386 else:
387 self.signer = OPTIONS.payload_signer
388 self.signer_args = OPTIONS.payload_signer_args
389
390 def Sign(self, in_file):
391 """Signs the given input file. Returns the output filename."""
392 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
393 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
394 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
395 stdoutdata, _ = signing.communicate()
396 assert signing.returncode == 0, \
397 "Failed to sign the input file: {}".format(stdoutdata)
398 return out_file
399
400
Tao Baoc7b403a2018-01-30 18:19:04 -0800401class Payload(object):
402 """Manages the creation and the signing of an A/B OTA Payload."""
403
404 PAYLOAD_BIN = 'payload.bin'
405 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800406 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
407 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Baoc7b403a2018-01-30 18:19:04 -0800408
Tao Bao667ff572018-02-10 00:02:40 -0800409 def __init__(self, secondary=False):
410 """Initializes a Payload instance.
411
412 Args:
413 secondary: Whether it's generating a secondary payload (default: False).
414 """
Tao Baoc7b403a2018-01-30 18:19:04 -0800415 # The place where the output from the subprocess should go.
416 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
417 self.payload_file = None
418 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800419 self.secondary = secondary
Tao Baoc7b403a2018-01-30 18:19:04 -0800420
421 def Generate(self, target_file, source_file=None, additional_args=None):
422 """Generates a payload from the given target-files zip(s).
423
424 Args:
425 target_file: The filename of the target build target-files zip.
426 source_file: The filename of the source build target-files zip; or None if
427 generating a full OTA.
428 additional_args: A list of additional args that should be passed to
429 brillo_update_payload script; or None.
430 """
431 if additional_args is None:
432 additional_args = []
433
434 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
435 cmd = ["brillo_update_payload", "generate",
436 "--payload", payload_file,
437 "--target_image", target_file]
438 if source_file is not None:
439 cmd.extend(["--source_image", source_file])
440 cmd.extend(additional_args)
441 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
442 stdoutdata, _ = p.communicate()
443 assert p.returncode == 0, \
444 "brillo_update_payload generate failed: {}".format(stdoutdata)
445
446 self.payload_file = payload_file
447 self.payload_properties = None
448
449 def Sign(self, payload_signer):
450 """Generates and signs the hashes of the payload and metadata.
451
452 Args:
453 payload_signer: A PayloadSigner() instance that serves the signing work.
454
455 Raises:
456 AssertionError: On any failure when calling brillo_update_payload script.
457 """
458 assert isinstance(payload_signer, PayloadSigner)
459
460 # 1. Generate hashes of the payload and metadata files.
461 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
462 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
463 cmd = ["brillo_update_payload", "hash",
464 "--unsigned_payload", self.payload_file,
465 "--signature_size", "256",
466 "--metadata_hash_file", metadata_sig_file,
467 "--payload_hash_file", payload_sig_file]
468 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
469 p1.communicate()
470 assert p1.returncode == 0, "brillo_update_payload hash failed"
471
472 # 2. Sign the hashes.
473 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
474 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
475
476 # 3. Insert the signatures back into the payload file.
477 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
478 suffix=".bin")
479 cmd = ["brillo_update_payload", "sign",
480 "--unsigned_payload", self.payload_file,
481 "--payload", signed_payload_file,
482 "--signature_size", "256",
483 "--metadata_signature_file", signed_metadata_sig_file,
484 "--payload_signature_file", signed_payload_sig_file]
485 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
486 p1.communicate()
487 assert p1.returncode == 0, "brillo_update_payload sign failed"
488
489 # 4. Dump the signed payload properties.
490 properties_file = common.MakeTempFile(prefix="payload-properties-",
491 suffix=".txt")
492 cmd = ["brillo_update_payload", "properties",
493 "--payload", signed_payload_file,
494 "--properties_file", properties_file]
495 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
496 p1.communicate()
497 assert p1.returncode == 0, "brillo_update_payload properties failed"
498
Tao Bao667ff572018-02-10 00:02:40 -0800499 if self.secondary:
500 with open(properties_file, "a") as f:
501 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
502
Tao Baoc7b403a2018-01-30 18:19:04 -0800503 if OPTIONS.wipe_user_data:
504 with open(properties_file, "a") as f:
505 f.write("POWERWASH=1\n")
506
507 self.payload_file = signed_payload_file
508 self.payload_properties = properties_file
509
Tao Bao667ff572018-02-10 00:02:40 -0800510 def WriteToZip(self, output_zip):
Tao Baoc7b403a2018-01-30 18:19:04 -0800511 """Writes the payload to the given zip.
512
513 Args:
514 output_zip: The output ZipFile instance.
515 """
516 assert self.payload_file is not None
517 assert self.payload_properties is not None
518
Tao Bao667ff572018-02-10 00:02:40 -0800519 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800520 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
521 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
522 else:
523 payload_arcname = Payload.PAYLOAD_BIN
524 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
525
Tao Baoc7b403a2018-01-30 18:19:04 -0800526 # Add the signed payload file and properties into the zip. In order to
527 # support streaming, we pack them as ZIP_STORED. So these entries can be
528 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800529 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Baoc7b403a2018-01-30 18:19:04 -0800530 compress_type=zipfile.ZIP_STORED)
531 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800532 arcname=payload_properties_arcname,
Tao Baoc7b403a2018-01-30 18:19:04 -0800533 compress_type=zipfile.ZIP_STORED)
534
535
Doug Zongkereef39442009-04-02 12:14:19 -0700536def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200537 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700538
Doug Zongker951495f2009-08-14 12:44:19 -0700539 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
540 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700541
542
Tao Bao481bab82017-12-21 11:23:09 -0800543def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800544 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800545 if not oem_source:
546 return None
547
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800548 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800549 for oem_file in oem_source:
550 with open(oem_file) as fp:
551 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800552 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700553
Doug Zongkereef39442009-04-02 12:14:19 -0700554
Tao Baod42e97e2016-11-30 12:11:57 -0800555def _WriteRecoveryImageToBoot(script, output_zip):
556 """Find and write recovery image to /boot in two-step OTA.
557
558 In two-step OTAs, we write recovery image to /boot as the first step so that
559 we can reboot to there and install a new recovery image to /recovery.
560 A special "recovery-two-step.img" will be preferred, which encodes the correct
561 path of "/boot". Otherwise the device may show "device is corrupt" message
562 when booting into /boot.
563
564 Fall back to using the regular recovery.img if the two-step recovery image
565 doesn't exist. Note that rebuilding the special image at this point may be
566 infeasible, because we don't have the desired boot signer and keys when
567 calling ota_from_target_files.py.
568 """
569
570 recovery_two_step_img_name = "recovery-two-step.img"
571 recovery_two_step_img_path = os.path.join(
572 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
573 if os.path.exists(recovery_two_step_img_path):
574 recovery_two_step_img = common.GetBootableImage(
575 recovery_two_step_img_name, recovery_two_step_img_name,
576 OPTIONS.input_tmp, "RECOVERY")
577 common.ZipWriteStr(
578 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800579 print("two-step package: using %s in stage 1/3" % (
580 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800581 script.WriteRawImage("/boot", recovery_two_step_img_name)
582 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800583 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800584 # The "recovery.img" entry has been written into package earlier.
585 script.WriteRawImage("/boot", "recovery.img")
586
587
Doug Zongkerc9253822014-02-04 12:17:58 -0800588def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700589 namelist = [name for name in target_files_zip.namelist()]
590 return ("SYSTEM/recovery-from-boot.p" in namelist or
591 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700592
Tao Bao457cbf62017-03-06 09:56:01 -0800593
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700594def HasVendorPartition(target_files_zip):
595 try:
596 target_files_zip.getinfo("VENDOR/")
597 return True
598 except KeyError:
599 return False
600
Tao Bao457cbf62017-03-06 09:56:01 -0800601
Tao Bao481bab82017-12-21 11:23:09 -0800602def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700603 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800604 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700605
606
Tao Bao481bab82017-12-21 11:23:09 -0800607def WriteFingerprintAssertion(script, target_info, source_info):
608 source_oem_props = source_info.oem_props
609 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700610
Tao Bao481bab82017-12-21 11:23:09 -0800611 if source_oem_props is None and target_oem_props is None:
612 script.AssertSomeFingerprint(
613 source_info.fingerprint, target_info.fingerprint)
614 elif source_oem_props is not None and target_oem_props is not None:
615 script.AssertSomeThumbprint(
616 target_info.GetBuildProp("ro.build.thumbprint"),
617 source_info.GetBuildProp("ro.build.thumbprint"))
618 elif source_oem_props is None and target_oem_props is not None:
619 script.AssertFingerprintOrThumbprint(
620 source_info.fingerprint,
621 target_info.GetBuildProp("ro.build.thumbprint"))
622 else:
623 script.AssertFingerprintOrThumbprint(
624 target_info.fingerprint,
625 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700626
Doug Zongkerfc44a512014-08-26 13:10:25 -0700627
Tao Bao481bab82017-12-21 11:23:09 -0800628def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
629 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700630 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700631
632 Metadata used for on-device compatibility verification is retrieved from
633 target_zip then added to compatibility.zip which is added to the output_zip
634 archive.
635
Tao Baobcd1d162017-08-26 13:10:26 -0700636 Compatibility archive should only be included for devices that have enabled
637 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700638
639 Args:
640 target_zip: Zip file containing the source files to be included for OTA.
641 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800642 target_info: The BuildInfo instance that holds the target build info.
643 source_info: The BuildInfo instance that holds the source build info, if
644 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700645 """
646
Tao Baobcd1d162017-08-26 13:10:26 -0700647 def AddCompatibilityArchive(system_updated, vendor_updated):
648 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700649
Tao Baobcd1d162017-08-26 13:10:26 -0700650 Args:
651 system_updated: If True, the system image will be updated and therefore
652 its metadata should be included.
653 vendor_updated: If True, the vendor image will be updated and therefore
654 its metadata should be included.
655 """
656 # Determine what metadata we need. Files are names relative to META/.
657 compatibility_files = []
658 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
659 system_metadata = ("system_manifest.xml", "system_matrix.xml")
660 if vendor_updated:
661 compatibility_files += vendor_metadata
662 if system_updated:
663 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700664
Tao Baobcd1d162017-08-26 13:10:26 -0700665 # Create new archive.
666 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800667 compatibility_archive_zip = zipfile.ZipFile(
668 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700669
Tao Baobcd1d162017-08-26 13:10:26 -0700670 # Add metadata.
671 for file_name in compatibility_files:
672 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700673
Tao Baobcd1d162017-08-26 13:10:26 -0700674 if target_file_name in target_zip.namelist():
675 data = target_zip.read(target_file_name)
676 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700677
Tao Baobcd1d162017-08-26 13:10:26 -0700678 # Ensure files are written before we copy into output_zip.
679 compatibility_archive_zip.close()
680
681 # Only add the archive if we have any compatibility info.
682 if compatibility_archive_zip.namelist():
683 common.ZipWrite(output_zip, compatibility_archive.name,
684 arcname="compatibility.zip",
685 compress_type=zipfile.ZIP_STORED)
686
687 # Will only proceed if the target has enabled the Treble support (as well as
688 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800689 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700690 return
691
692 # We don't support OEM thumbprint in Treble world (which calculates
693 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800694 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700695
696 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800697 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700698 AddCompatibilityArchive(True, True)
699 return
700
Tao Bao481bab82017-12-21 11:23:09 -0800701 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700702
Tao Bao481bab82017-12-21 11:23:09 -0800703 source_fp = source_info.fingerprint
704 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700705 system_updated = source_fp != target_fp
706
Tao Bao481bab82017-12-21 11:23:09 -0800707 source_fp_vendor = source_info.GetVendorBuildProp(
708 "ro.vendor.build.fingerprint")
709 target_fp_vendor = target_info.GetVendorBuildProp(
710 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700711 vendor_updated = source_fp_vendor != target_fp_vendor
712
713 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700714
715
Tao Baoc0746f42018-02-21 13:17:22 -0800716def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800717 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700718
Tao Bao481bab82017-12-21 11:23:09 -0800719 # We don't know what version it will be installed on top of. We expect the API
720 # just won't change very often. Similarly for fstab, it might have changed in
721 # the target build.
722 target_api_version = target_info["recovery_api_version"]
723 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700724
Tao Bao481bab82017-12-21 11:23:09 -0800725 if target_info.oem_props and not OPTIONS.oem_no_mount:
726 target_info.WriteMountOemScript(script)
727
Tao Baodf3a48b2018-01-10 16:30:43 -0800728 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700729
Tao Baoc0746f42018-02-21 13:17:22 -0800730 if not OPTIONS.no_signing:
731 staging_file = common.MakeTempFile(suffix='.zip')
732 else:
733 staging_file = output_file
734
735 output_zip = zipfile.ZipFile(
736 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
737
Doug Zongker05d3dea2009-06-22 11:32:31 -0700738 device_specific = common.DeviceSpecificParams(
739 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800740 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700741 output_zip=output_zip,
742 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700743 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700744 metadata=metadata,
745 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700746
Tao Bao457cbf62017-03-06 09:56:01 -0800747 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800748
Tao Bao481bab82017-12-21 11:23:09 -0800749 # Assertions (e.g. downgrade check, device properties check).
750 ts = target_info.GetBuildProp("ro.build.date.utc")
751 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700752 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700753
Tao Bao481bab82017-12-21 11:23:09 -0800754 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700755 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800756
757 # Two-step package strategy (in chronological order, which is *not*
758 # the order in which the generated script has things):
759 #
760 # if stage is not "2/3" or "3/3":
761 # write recovery image to boot partition
762 # set stage to "2/3"
763 # reboot to boot partition and restart recovery
764 # else if stage is "2/3":
765 # write recovery image to recovery partition
766 # set stage to "3/3"
767 # reboot to recovery partition and restart recovery
768 # else:
769 # (stage must be "3/3")
770 # set stage to ""
771 # do normal full package installation:
772 # wipe and install system, boot image, etc.
773 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700774 # complete script normally
775 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800776
777 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
778 OPTIONS.input_tmp, "RECOVERY")
779 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800780 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800781 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800782 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800783 assert fs.fs_type.upper() == "EMMC", \
784 "two-step packages only supported on devices with EMMC /misc partitions"
785 bcb_dev = {"bcb_dev": fs.device}
786 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
787 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700788if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800789""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800790
791 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
792 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800793 script.WriteRawImage("/recovery", "recovery.img")
794 script.AppendExtra("""
795set_stage("%(bcb_dev)s", "3/3");
796reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700797else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800798""" % bcb_dev)
799
Tao Baod42e97e2016-11-30 12:11:57 -0800800 # Stage 3/3: Make changes.
801 script.Comment("Stage 3/3")
802
Tao Bao6c55a8a2015-04-08 15:30:27 -0700803 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800804 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700805
Doug Zongkere5ff5902012-01-17 10:55:37 -0800806 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700807
Doug Zongker01ce19c2014-02-04 13:48:15 -0800808 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700809
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700810 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800811 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700812 if HasVendorPartition(input_zip):
813 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700814
Doug Zongker4b9596f2014-06-09 14:15:45 -0700815 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800816
Tao Baoe709b092018-02-07 12:40:00 -0800817 # See the notes in WriteBlockIncrementalOTAPackage().
818 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
819
Tao Bao457cbf62017-03-06 09:56:01 -0800820 # Full OTA is done as an "incremental" against an empty source image. This
821 # has the effect of writing new data from the package to the entire
822 # partition, but lets us reuse the updater code that writes incrementals to
823 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800824 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
825 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800826 system_tgt.ResetFileMap()
827 system_diff = common.BlockDifference("system", system_tgt, src=None)
828 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700829
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700830 boot_img = common.GetBootableImage(
831 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800832
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700833 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700834 script.ShowProgress(0.1, 0)
835
Tao Baoe709b092018-02-07 12:40:00 -0800836 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
837 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800838 vendor_tgt.ResetFileMap()
839 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
840 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700841
Tao Bao481bab82017-12-21 11:23:09 -0800842 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700843
Tao Bao481bab82017-12-21 11:23:09 -0800844 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700845 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700846
Doug Zongker01ce19c2014-02-04 13:48:15 -0800847 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700848 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700849
Doug Zongker01ce19c2014-02-04 13:48:15 -0800850 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700851 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700852
Doug Zongker1c390a22009-05-14 19:06:36 -0700853 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700854 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700855
Doug Zongker14833602010-02-02 13:12:04 -0800856 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800857
Doug Zongker922206e2014-03-04 13:16:24 -0800858 if OPTIONS.wipe_user_data:
859 script.ShowProgress(0.1, 10)
860 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700861
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800862 if OPTIONS.two_step:
863 script.AppendExtra("""
864set_stage("%(bcb_dev)s", "");
865""" % bcb_dev)
866 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800867
868 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
869 script.Comment("Stage 1/3")
870 _WriteRecoveryImageToBoot(script, output_zip)
871
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800872 script.AppendExtra("""
873set_stage("%(bcb_dev)s", "2/3");
874reboot_now("%(bcb_dev)s", "");
875endif;
876endif;
877""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800878
Tao Bao5d182562016-02-23 11:38:39 -0800879 script.SetProgress(1)
880 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800881 metadata["ota-required-cache"] = str(script.required_cache)
Tao Baoc0746f42018-02-21 13:17:22 -0800882
883 # We haven't written the metadata entry, which will be done in
884 # FinalizeMetadata.
885 common.ZipClose(output_zip)
886
887 needed_property_files = (
888 NonAbOtaPropertyFiles(),
889 )
890 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700891
Doug Zongkerfc44a512014-08-26 13:10:25 -0700892
Doug Zongker2ea21062010-04-28 16:05:21 -0700893def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800894 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
895 common.ZipWriteStr(output_zip, METADATA_NAME, value,
896 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700897
Doug Zongkerfc44a512014-08-26 13:10:25 -0700898
Tao Bao481bab82017-12-21 11:23:09 -0800899def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800900 # Only incremental OTAs are allowed to reach here.
901 assert OPTIONS.incremental_source is not None
902
Tao Bao481bab82017-12-21 11:23:09 -0800903 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
904 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800905 is_downgrade = long(post_timestamp) < long(pre_timestamp)
906
907 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800908 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700909 raise RuntimeError(
910 "--downgrade or --override_timestamp specified but no downgrade "
911 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800912 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800913 else:
914 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700915 raise RuntimeError(
916 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
917 "Need to specify --override_timestamp OR --downgrade to allow "
918 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800919
920
Tao Baodf3a48b2018-01-10 16:30:43 -0800921def GetPackageMetadata(target_info, source_info=None):
922 """Generates and returns the metadata dict.
923
924 It generates a dict() that contains the info to be written into an OTA
925 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700926 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800927
928 Args:
929 target_info: The BuildInfo instance that holds the target build info.
930 source_info: The BuildInfo instance that holds the source build info, or
931 None if generating full OTA.
932
933 Returns:
934 A dict to be written into package metadata entry.
935 """
936 assert isinstance(target_info, BuildInfo)
937 assert source_info is None or isinstance(source_info, BuildInfo)
938
939 metadata = {
940 'post-build' : target_info.fingerprint,
941 'post-build-incremental' : target_info.GetBuildProp(
942 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800943 'post-sdk-level' : target_info.GetBuildProp(
944 'ro.build.version.sdk'),
945 'post-security-patch-level' : target_info.GetBuildProp(
946 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800947 }
948
949 if target_info.is_ab:
950 metadata['ota-type'] = 'AB'
951 metadata['ota-required-cache'] = '0'
952 else:
953 metadata['ota-type'] = 'BLOCK'
954
955 if OPTIONS.wipe_user_data:
956 metadata['ota-wipe'] = 'yes'
957
958 is_incremental = source_info is not None
959 if is_incremental:
960 metadata['pre-build'] = source_info.fingerprint
961 metadata['pre-build-incremental'] = source_info.GetBuildProp(
962 'ro.build.version.incremental')
963 metadata['pre-device'] = source_info.device
964 else:
965 metadata['pre-device'] = target_info.device
966
Tao Baofaa8e0b2018-04-12 14:31:43 -0700967 # Use the actual post-timestamp, even for a downgrade case.
968 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
969
970 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800971 if is_incremental:
972 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800973
974 return metadata
975
976
Tao Bao69203522018-03-08 16:09:01 -0800977class PropertyFiles(object):
978 """A class that computes the property-files string for an OTA package.
979
980 A property-files string is a comma-separated string that contains the
981 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
982 can be fetched directly with the package URL along with the offset/size info.
983 These strings can be used for streaming A/B OTAs, or allowing an updater to
984 download package metadata entry directly, without paying the cost of
985 downloading entire package.
Tao Baof5110492018-03-02 09:47:43 -0800986
Tao Baoae5e4c32018-03-01 19:30:00 -0800987 Computing the final property-files string requires two passes. Because doing
988 the whole package signing (with signapk.jar) will possibly reorder the ZIP
989 entries, which may in turn invalidate earlier computed ZIP entry offset/size
990 values.
991
992 This class provides functions to be called for each pass. The general flow is
993 as follows.
994
Tao Bao69203522018-03-08 16:09:01 -0800995 property_files = PropertyFiles()
Tao Baoae5e4c32018-03-01 19:30:00 -0800996 # The first pass, which writes placeholders before doing initial signing.
997 property_files.Compute()
998 SignOutput()
999
1000 # The second pass, by replacing the placeholders with actual data.
1001 property_files.Finalize()
1002 SignOutput()
1003
1004 And the caller can additionally verify the final result.
1005
1006 property_files.Verify()
Tao Baof5110492018-03-02 09:47:43 -08001007 """
1008
Tao Baoae5e4c32018-03-01 19:30:00 -08001009 def __init__(self):
Tao Bao69203522018-03-08 16:09:01 -08001010 self.name = None
1011 self.required = ()
1012 self.optional = ()
Tao Baof5110492018-03-02 09:47:43 -08001013
Tao Baoae5e4c32018-03-01 19:30:00 -08001014 def Compute(self, input_zip):
1015 """Computes and returns a property-files string with placeholders.
Tao Baof5110492018-03-02 09:47:43 -08001016
Tao Baoae5e4c32018-03-01 19:30:00 -08001017 We reserve extra space for the offset and size of the metadata entry itself,
1018 although we don't know the final values until the package gets signed.
Tao Baof5110492018-03-02 09:47:43 -08001019
Tao Baoae5e4c32018-03-01 19:30:00 -08001020 Args:
1021 input_zip: The input ZIP file.
Tao Baof5110492018-03-02 09:47:43 -08001022
Tao Baoae5e4c32018-03-01 19:30:00 -08001023 Returns:
1024 A string with placeholders for the metadata offset/size info, e.g.
1025 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1026 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001027 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baof5110492018-03-02 09:47:43 -08001028
Tao Bao3bf8c652018-03-16 12:59:42 -07001029 class InsufficientSpaceException(Exception):
1030 pass
1031
Tao Baoae5e4c32018-03-01 19:30:00 -08001032 def Finalize(self, input_zip, reserved_length):
1033 """Finalizes a property-files string with actual METADATA offset/size info.
1034
1035 The input ZIP file has been signed, with the ZIP entries in the desired
1036 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1037 the ZIP entry offsets and construct the property-files string with actual
1038 data. Note that during this process, we must pad the property-files string
1039 to the reserved length, so that the METADATA entry size remains the same.
1040 Otherwise the entries' offsets and sizes may change again.
1041
1042 Args:
1043 input_zip: The input ZIP file.
1044 reserved_length: The reserved length of the property-files string during
1045 the call to Compute(). The final string must be no more than this
1046 size.
1047
1048 Returns:
1049 A property-files string including the metadata offset/size info, e.g.
1050 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1051
1052 Raises:
Tao Bao3bf8c652018-03-16 12:59:42 -07001053 InsufficientSpaceException: If the reserved length is insufficient to hold
1054 the final string.
Tao Baoae5e4c32018-03-01 19:30:00 -08001055 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001056 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Bao3bf8c652018-03-16 12:59:42 -07001057 if len(result) > reserved_length:
1058 raise self.InsufficientSpaceException(
1059 'Insufficient reserved space: reserved={}, actual={}'.format(
1060 reserved_length, len(result)))
1061
Tao Baoae5e4c32018-03-01 19:30:00 -08001062 result += ' ' * (reserved_length - len(result))
1063 return result
1064
1065 def Verify(self, input_zip, expected):
1066 """Verifies the input ZIP file contains the expected property-files string.
1067
1068 Args:
1069 input_zip: The input ZIP file.
1070 expected: The property-files string that's computed from Finalize().
1071
1072 Raises:
1073 AssertionError: On finding a mismatch.
1074 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001075 actual = self.GetPropertyFilesString(input_zip)
Tao Baoae5e4c32018-03-01 19:30:00 -08001076 assert actual == expected, \
1077 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1078
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001079 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1080 """
1081 Constructs the property-files string per request.
1082
1083 Args:
1084 zip_file: The input ZIP file.
1085 reserved_length: The reserved length of the property-files string.
1086
1087 Returns:
1088 A property-files string including the metadata offset/size info, e.g.
1089 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1090 """
Tao Baoae5e4c32018-03-01 19:30:00 -08001091
1092 def ComputeEntryOffsetSize(name):
1093 """Computes the zip entry offset and size."""
1094 info = zip_file.getinfo(name)
1095 offset = info.header_offset + len(info.FileHeader())
1096 size = info.file_size
1097 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1098
1099 tokens = []
Tao Baob6304672018-03-08 16:28:33 -08001100 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baoae5e4c32018-03-01 19:30:00 -08001101 for entry in self.required:
1102 tokens.append(ComputeEntryOffsetSize(entry))
1103 for entry in self.optional:
1104 if entry in zip_file.namelist():
1105 tokens.append(ComputeEntryOffsetSize(entry))
1106
1107 # 'META-INF/com/android/metadata' is required. We don't know its actual
1108 # offset and length (as well as the values for other entries). So we reserve
Tao Bao3bf8c652018-03-16 12:59:42 -07001109 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1110 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1111 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1112 # reserved space serves the metadata entry only.
Tao Baoae5e4c32018-03-01 19:30:00 -08001113 if reserve_space:
Tao Bao3bf8c652018-03-16 12:59:42 -07001114 tokens.append('metadata:' + ' ' * 15)
Tao Baoae5e4c32018-03-01 19:30:00 -08001115 else:
1116 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1117
1118 return ','.join(tokens)
Tao Baof5110492018-03-02 09:47:43 -08001119
Tao Baob6304672018-03-08 16:28:33 -08001120 def _GetPrecomputed(self, input_zip):
1121 """Computes the additional tokens to be included into the property-files.
1122
1123 This applies to tokens without actual ZIP entries, such as
1124 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1125 that they can download the payload metadata directly with the info.
1126
1127 Args:
1128 input_zip: The input zip file.
1129
1130 Returns:
1131 A list of strings (tokens) to be added to the property-files string.
1132 """
1133 # pylint: disable=no-self-use
1134 # pylint: disable=unused-argument
1135 return []
1136
Tao Baof5110492018-03-02 09:47:43 -08001137
Tao Bao69203522018-03-08 16:09:01 -08001138class StreamingPropertyFiles(PropertyFiles):
1139 """A subclass for computing the property-files for streaming A/B OTAs."""
1140
1141 def __init__(self):
1142 super(StreamingPropertyFiles, self).__init__()
1143 self.name = 'ota-streaming-property-files'
1144 self.required = (
1145 # payload.bin and payload_properties.txt must exist.
1146 'payload.bin',
1147 'payload_properties.txt',
1148 )
1149 self.optional = (
1150 # care_map.txt is available only if dm-verity is enabled.
1151 'care_map.txt',
1152 # compatibility.zip is available only if target supports Treble.
1153 'compatibility.zip',
1154 )
1155
1156
Tao Baob6304672018-03-08 16:28:33 -08001157class AbOtaPropertyFiles(StreamingPropertyFiles):
1158 """The property-files for A/B OTA that includes payload_metadata.bin info.
1159
1160 Since P, we expose one more token (aka property-file), in addition to the ones
1161 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1162 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1163 doesn't exist as a separate ZIP entry, but can be used to verify if the
1164 payload can be applied on the given device.
1165
1166 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1167 and the newly added 'ota-property-files' in P. The new token will only be
1168 available in 'ota-property-files'.
1169 """
1170
1171 def __init__(self):
1172 super(AbOtaPropertyFiles, self).__init__()
1173 self.name = 'ota-property-files'
1174
1175 def _GetPrecomputed(self, input_zip):
1176 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1177 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1178
1179 @staticmethod
1180 def _GetPayloadMetadataOffsetAndSize(input_zip):
1181 """Computes the offset and size of the payload metadata for a given package.
1182
1183 (From system/update_engine/update_metadata.proto)
1184 A delta update file contains all the deltas needed to update a system from
1185 one specific version to another specific version. The update format is
1186 represented by this struct pseudocode:
1187
1188 struct delta_update_file {
1189 char magic[4] = "CrAU";
1190 uint64 file_format_version;
1191 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1192
1193 // Only present if format_version > 1:
1194 uint32 metadata_signature_size;
1195
1196 // The Bzip2 compressed DeltaArchiveManifest
1197 char manifest[metadata_signature_size];
1198
1199 // The signature of the metadata (from the beginning of the payload up to
1200 // this location, not including the signature itself). This is a
1201 // serialized Signatures message.
1202 char medatada_signature_message[metadata_signature_size];
1203
1204 // Data blobs for files, no specific format. The specific offset
1205 // and length of each data blob is recorded in the DeltaArchiveManifest.
1206 struct {
1207 char data[];
1208 } blobs[];
1209
1210 // These two are not signed:
1211 uint64 payload_signatures_message_size;
1212 char payload_signatures_message[];
1213 };
1214
1215 'payload-metadata.bin' contains all the bytes from the beginning of the
1216 payload, till the end of 'medatada_signature_message'.
1217 """
1218 payload_info = input_zip.getinfo('payload.bin')
1219 payload_offset = payload_info.header_offset + len(payload_info.FileHeader())
1220 payload_size = payload_info.file_size
1221
1222 with input_zip.open('payload.bin', 'r') as payload_fp:
1223 header_bin = payload_fp.read(24)
1224
1225 # network byte order (big-endian)
1226 header = struct.unpack("!IQQL", header_bin)
1227
1228 # 'CrAU'
1229 magic = header[0]
1230 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1231
1232 manifest_size = header[2]
1233 metadata_signature_size = header[3]
1234 metadata_total = 24 + manifest_size + metadata_signature_size
1235 assert metadata_total < payload_size
1236
1237 return (payload_offset, metadata_total)
1238
1239
Tao Baoc0746f42018-02-21 13:17:22 -08001240class NonAbOtaPropertyFiles(PropertyFiles):
1241 """The property-files for non-A/B OTA.
1242
1243 For non-A/B OTA, the property-files string contains the info for METADATA
1244 entry, with which a system updater can be fetched the package metadata prior
1245 to downloading the entire package.
1246 """
1247
1248 def __init__(self):
1249 super(NonAbOtaPropertyFiles, self).__init__()
1250 self.name = 'ota-property-files'
1251
1252
Tao Bao69203522018-03-08 16:09:01 -08001253def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baof5110492018-03-02 09:47:43 -08001254 """Finalizes the metadata and signs an A/B OTA package.
1255
1256 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1257 that contains the offsets and sizes for the ZIP entries. An example
1258 property-files string is as follows.
1259
1260 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1261
1262 OTA server can pass down this string, in addition to the package URL, to the
1263 system update client. System update client can then fetch individual ZIP
1264 entries (ZIP_STORED) directly at the given offset of the URL.
1265
1266 Args:
1267 metadata: The metadata dict for the package.
1268 input_file: The input ZIP filename that doesn't contain the package METADATA
1269 entry yet.
1270 output_file: The final output ZIP filename.
Tao Bao69203522018-03-08 16:09:01 -08001271 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baof5110492018-03-02 09:47:43 -08001272 """
Tao Baof5110492018-03-02 09:47:43 -08001273
Tao Bao3bf8c652018-03-16 12:59:42 -07001274 def ComputeAllPropertyFiles(input_file, needed_property_files):
1275 # Write the current metadata entry with placeholders.
1276 with zipfile.ZipFile(input_file) as input_zip:
1277 for property_files in needed_property_files:
1278 metadata[property_files.name] = property_files.Compute(input_zip)
1279 namelist = input_zip.namelist()
Tao Baof5110492018-03-02 09:47:43 -08001280
Tao Bao3bf8c652018-03-16 12:59:42 -07001281 if METADATA_NAME in namelist:
1282 common.ZipDelete(input_file, METADATA_NAME)
1283 output_zip = zipfile.ZipFile(input_file, 'a')
1284 WriteMetadata(metadata, output_zip)
1285 common.ZipClose(output_zip)
1286
1287 if OPTIONS.no_signing:
1288 return input_file
1289
Tao Baoc0746f42018-02-21 13:17:22 -08001290 prelim_signing = common.MakeTempFile(suffix='.zip')
1291 SignOutput(input_file, prelim_signing)
Tao Bao3bf8c652018-03-16 12:59:42 -07001292 return prelim_signing
Tao Baof5110492018-03-02 09:47:43 -08001293
Tao Bao3bf8c652018-03-16 12:59:42 -07001294 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1295 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1296 for property_files in needed_property_files:
1297 metadata[property_files.name] = property_files.Finalize(
1298 prelim_signing_zip, len(metadata[property_files.name]))
1299
1300 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1301 # entries, as well as padding the entry headers. We do a preliminary signing
1302 # (with an incomplete metadata entry) to allow that to happen. Then compute
1303 # the ZIP entry offsets, write back the final metadata and do the final
1304 # signing.
1305 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1306 try:
1307 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1308 except PropertyFiles.InsufficientSpaceException:
1309 # Even with the preliminary signing, the entry orders may change
1310 # dramatically, which leads to insufficiently reserved space during the
1311 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1312 # preliminary signing works, based on the already ordered ZIP entries, to
1313 # address the issue.
1314 prelim_signing = ComputeAllPropertyFiles(
1315 prelim_signing, needed_property_files)
1316 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baof5110492018-03-02 09:47:43 -08001317
1318 # Replace the METADATA entry.
1319 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Bao3bf8c652018-03-16 12:59:42 -07001320 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baof5110492018-03-02 09:47:43 -08001321 WriteMetadata(metadata, output_zip)
1322 common.ZipClose(output_zip)
1323
1324 # Re-sign the package after updating the metadata entry.
Tao Baoc0746f42018-02-21 13:17:22 -08001325 if OPTIONS.no_signing:
1326 output_file = prelim_signing
1327 else:
1328 SignOutput(prelim_signing, output_file)
Tao Baof5110492018-03-02 09:47:43 -08001329
1330 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao3bf8c652018-03-16 12:59:42 -07001331 with zipfile.ZipFile(output_file) as output_zip:
Tao Bao69203522018-03-08 16:09:01 -08001332 for property_files in needed_property_files:
1333 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baof5110492018-03-02 09:47:43 -08001334
1335
Tao Baoc0746f42018-02-21 13:17:22 -08001336def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001337 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1338 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001339
Tao Bao481bab82017-12-21 11:23:09 -08001340 target_api_version = target_info["recovery_api_version"]
1341 source_api_version = source_info["recovery_api_version"]
1342 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001343 print("WARNING: generating edify script for a source that "
1344 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001345
Tao Bao481bab82017-12-21 11:23:09 -08001346 script = edify_generator.EdifyGenerator(
1347 source_api_version, target_info, fstab=source_info["fstab"])
1348
1349 if target_info.oem_props or source_info.oem_props:
1350 if not OPTIONS.oem_no_mount:
1351 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001352
Tao Baodf3a48b2018-01-10 16:30:43 -08001353 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001354
Tao Baoc0746f42018-02-21 13:17:22 -08001355 if not OPTIONS.no_signing:
1356 staging_file = common.MakeTempFile(suffix='.zip')
1357 else:
1358 staging_file = output_file
1359
1360 output_zip = zipfile.ZipFile(
1361 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1362
Geremy Condra36bd3652014-02-06 19:45:10 -08001363 device_specific = common.DeviceSpecificParams(
1364 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001365 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001366 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001367 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001368 output_zip=output_zip,
1369 script=script,
1370 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001371 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001372
Geremy Condra36bd3652014-02-06 19:45:10 -08001373 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001374 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001375 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001376 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001377 updating_boot = (not OPTIONS.two_step and
1378 (source_boot.data != target_boot.data))
1379
Geremy Condra36bd3652014-02-06 19:45:10 -08001380 target_recovery = common.GetBootableImage(
1381 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001382
Tao Baoe709b092018-02-07 12:40:00 -08001383 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1384 # shared blocks (i.e. some blocks will show up in multiple files' block
1385 # list). We can only allocate such shared blocks to the first "owner", and
1386 # disable imgdiff for all later occurrences.
1387 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1388 target_info.get('ext4_share_dup_blocks') == "true")
1389 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1390 allow_shared_blocks)
1391 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
1392 allow_shared_blocks)
Tao Baodd2a5892015-03-12 12:32:37 -07001393
Tao Bao0582cb62017-12-21 11:47:01 -08001394 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001395 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001396 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001397
Tao Baof8acad12016-07-07 09:09:58 -07001398 # Check the first block of the source system partition for remount R/W only
1399 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001400 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001401 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001402 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1403 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1404 # b) the blocks listed in block map may not contain all the bytes for a given
1405 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001406 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001407 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1408 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001409 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001410 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001411 version=blockimgdiff_version,
1412 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001413
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001414 if HasVendorPartition(target_zip):
1415 if not HasVendorPartition(source_zip):
1416 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001417 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1418 allow_shared_blocks)
1419 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip,
1420 allow_shared_blocks)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001421
1422 # Check first block of vendor partition for remount R/W only if
1423 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001424 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001425 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001426 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001427 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001428 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001429 version=blockimgdiff_version,
1430 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001431 else:
1432 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001433
Tao Baobcd1d162017-08-26 13:10:26 -07001434 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001435 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001436
Tao Bao481bab82017-12-21 11:23:09 -08001437 # Assertions (e.g. device properties check).
1438 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001439 device_specific.IncrementalOTA_Assertions()
1440
1441 # Two-step incremental package strategy (in chronological order,
1442 # which is *not* the order in which the generated script has
1443 # things):
1444 #
1445 # if stage is not "2/3" or "3/3":
1446 # do verification on current system
1447 # write recovery image to boot partition
1448 # set stage to "2/3"
1449 # reboot to boot partition and restart recovery
1450 # else if stage is "2/3":
1451 # write recovery image to recovery partition
1452 # set stage to "3/3"
1453 # reboot to recovery partition and restart recovery
1454 # else:
1455 # (stage must be "3/3")
1456 # perform update:
1457 # patch system files, etc.
1458 # force full install of new boot image
1459 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001460 # complete script normally
1461 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001462
1463 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001464 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001465 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001466 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001467 assert fs.fs_type.upper() == "EMMC", \
1468 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001469 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001470 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1471 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001472if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001473""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001474
1475 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1476 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001477 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001478 script.WriteRawImage("/recovery", "recovery.img")
1479 script.AppendExtra("""
1480set_stage("%(bcb_dev)s", "3/3");
1481reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001482else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001483""" % bcb_dev)
1484
Tao Baod42e97e2016-11-30 12:11:57 -08001485 # Stage 1/3: (a) Verify the current system.
1486 script.Comment("Stage 1/3")
1487
Tao Bao6c55a8a2015-04-08 15:30:27 -07001488 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001489 script.Print("Source: {}".format(source_info.fingerprint))
1490 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001491
Geremy Condra36bd3652014-02-06 19:45:10 -08001492 script.Print("Verifying current system...")
1493
1494 device_specific.IncrementalOTA_VerifyBegin()
1495
Tao Bao481bab82017-12-21 11:23:09 -08001496 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001497
Tao Baod8d14be2016-02-04 14:26:02 -08001498 # Check the required cache size (i.e. stashed blocks).
1499 size = []
1500 if system_diff:
1501 size.append(system_diff.required_cache)
1502 if vendor_diff:
1503 size.append(vendor_diff.required_cache)
1504
Geremy Condra36bd3652014-02-06 19:45:10 -08001505 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001506 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001507 d = common.Difference(target_boot, source_boot)
1508 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001509 if d is None:
1510 include_full_boot = True
1511 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1512 else:
1513 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001514
Tao Bao89fbb0f2017-01-10 10:47:58 -08001515 print("boot target: %d source: %d diff: %d" % (
1516 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001517
Doug Zongkerf8340082014-08-05 10:39:37 -07001518 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001519
Doug Zongkerf8340082014-08-05 10:39:37 -07001520 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1521 (boot_type, boot_device,
1522 source_boot.size, source_boot.sha1,
1523 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001524 size.append(target_boot.size)
1525
1526 if size:
1527 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001528
1529 device_specific.IncrementalOTA_VerifyEnd()
1530
1531 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001532 # Stage 1/3: (b) Write recovery image to /boot.
1533 _WriteRecoveryImageToBoot(script, output_zip)
1534
Geremy Condra36bd3652014-02-06 19:45:10 -08001535 script.AppendExtra("""
1536set_stage("%(bcb_dev)s", "2/3");
1537reboot_now("%(bcb_dev)s", "");
1538else
1539""" % bcb_dev)
1540
Tao Baod42e97e2016-11-30 12:11:57 -08001541 # Stage 3/3: Make changes.
1542 script.Comment("Stage 3/3")
1543
Jesse Zhao75bcea02015-01-06 10:59:53 -08001544 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001545 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001546 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001547 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001548
Geremy Condra36bd3652014-02-06 19:45:10 -08001549 script.Comment("---- start making changes here ----")
1550
1551 device_specific.IncrementalOTA_InstallBegin()
1552
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001553 system_diff.WriteScript(script, output_zip,
1554 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001555
Doug Zongkerfc44a512014-08-26 13:10:25 -07001556 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001557 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001558
1559 if OPTIONS.two_step:
1560 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1561 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001562 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001563
1564 if not OPTIONS.two_step:
1565 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001566 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001567 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001568 script.Print("Installing boot image...")
1569 script.WriteRawImage("/boot", "boot.img")
1570 else:
1571 # Produce the boot image by applying a patch to the current
1572 # contents of the boot partition, and write it back to the
1573 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001574 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001575 script.Print("Patching boot image...")
1576 script.ShowProgress(0.1, 10)
1577 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1578 % (boot_type, boot_device,
1579 source_boot.size, source_boot.sha1,
1580 target_boot.size, target_boot.sha1),
1581 "-",
1582 target_boot.size, target_boot.sha1,
1583 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001584 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001585 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001586
1587 # Do device-specific installation (eg, write radio image).
1588 device_specific.IncrementalOTA_InstallEnd()
1589
1590 if OPTIONS.extra_script is not None:
1591 script.AppendExtra(OPTIONS.extra_script)
1592
Doug Zongker922206e2014-03-04 13:16:24 -08001593 if OPTIONS.wipe_user_data:
1594 script.Print("Erasing user data...")
1595 script.FormatPartition("/data")
1596
Geremy Condra36bd3652014-02-06 19:45:10 -08001597 if OPTIONS.two_step:
1598 script.AppendExtra("""
1599set_stage("%(bcb_dev)s", "");
1600endif;
1601endif;
1602""" % bcb_dev)
1603
1604 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001605 # For downgrade OTAs, we prefer to use the update-binary in the source
1606 # build that is actually newer than the one in the target build.
1607 if OPTIONS.downgrade:
1608 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1609 else:
1610 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001611 metadata["ota-required-cache"] = str(script.required_cache)
Tao Baoc0746f42018-02-21 13:17:22 -08001612
1613 # We haven't written the metadata entry yet, which will be handled in
1614 # FinalizeMetadata().
1615 common.ZipClose(output_zip)
1616
1617 # Sign the generated zip package unless no_signing is specified.
1618 needed_property_files = (
1619 NonAbOtaPropertyFiles(),
1620 )
1621 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001622
Doug Zongker32b527d2014-03-04 10:03:02 -08001623
Tao Bao15a146a2018-02-21 16:06:59 -08001624def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001625 """Returns a target-files.zip file for generating secondary payload.
1626
1627 Although the original target-files.zip already contains secondary slot
1628 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1629 ones without _other suffix. Note that we cannot instead modify the names in
1630 META/ab_partitions.txt, because there are no matching partitions on device.
1631
1632 For the partitions that don't have secondary images, the ones for primary
1633 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1634 bootloader images in the inactive slot.
1635
1636 Args:
1637 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001638 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001639
1640 Returns:
1641 The filename of the target-files.zip for generating secondary payload.
1642 """
1643 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1644 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1645
Tao Baodba59ee2018-01-09 13:21:02 -08001646 input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
1647 with zipfile.ZipFile(input_file, 'r') as input_zip:
1648 infolist = input_zip.infolist()
1649
1650 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001651 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1652 if info.filename == 'IMAGES/system_other.img':
1653 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1654
1655 # Primary images and friends need to be skipped explicitly.
1656 elif info.filename in ('IMAGES/system.img',
1657 'IMAGES/system.map'):
1658 pass
1659
Tao Bao15a146a2018-02-21 16:06:59 -08001660 # Skip copying the postinstall config if requested.
1661 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1662 pass
1663
Tao Baof7140c02018-01-30 17:09:24 -08001664 elif info.filename.startswith(('META/', 'IMAGES/')):
1665 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1666
Tao Baof7140c02018-01-30 17:09:24 -08001667 common.ZipClose(target_zip)
1668
1669 return target_file
1670
1671
Tao Bao15a146a2018-02-21 16:06:59 -08001672def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1673 """Returns a target-files.zip that's not containing postinstall_config.txt.
1674
1675 This allows brillo_update_payload script to skip writing all the postinstall
1676 hooks in the generated payload. The input target-files.zip file will be
1677 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1678 contain the postinstall_config.txt entry, the input file will be returned.
1679
1680 Args:
1681 input_file: The input target-files.zip filename.
1682
1683 Returns:
1684 The filename of target-files.zip that doesn't contain postinstall config.
1685 """
1686 # We should only make a copy if postinstall_config entry exists.
1687 with zipfile.ZipFile(input_file, 'r') as input_zip:
1688 if POSTINSTALL_CONFIG not in input_zip.namelist():
1689 return input_file
1690
1691 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1692 shutil.copyfile(input_file, target_file)
1693 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1694 return target_file
1695
1696
Tao Baoc098e9e2016-01-07 13:03:56 -08001697def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1698 source_file=None):
Tao Baof5110492018-03-02 09:47:43 -08001699 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001700 # Stage the output zip package for package signing.
Tao Baoc0746f42018-02-21 13:17:22 -08001701 if not OPTIONS.no_signing:
1702 staging_file = common.MakeTempFile(suffix='.zip')
1703 else:
1704 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001705 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001706 compression=zipfile.ZIP_DEFLATED)
1707
Tao Bao481bab82017-12-21 11:23:09 -08001708 if source_file is not None:
1709 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1710 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1711 else:
1712 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1713 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001714
Tao Bao481bab82017-12-21 11:23:09 -08001715 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001716 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001717
Tao Bao15a146a2018-02-21 16:06:59 -08001718 if OPTIONS.skip_postinstall:
1719 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1720
Tao Baoc7b403a2018-01-30 18:19:04 -08001721 # Generate payload.
1722 payload = Payload()
1723 payload.Generate(target_file, source_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001724
Tao Baoc7b403a2018-01-30 18:19:04 -08001725 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001726 payload_signer = PayloadSigner()
1727 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001728
Tao Baoc7b403a2018-01-30 18:19:04 -08001729 # Write the payload into output zip.
1730 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001731
Tao Baof7140c02018-01-30 17:09:24 -08001732 # Generate and include the secondary payload that installs secondary images
1733 # (e.g. system_other.img).
1734 if OPTIONS.include_secondary:
1735 # We always include a full payload for the secondary slot, even when
1736 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001737 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1738 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001739 secondary_payload = Payload(secondary=True)
Tao Baof7140c02018-01-30 17:09:24 -08001740 secondary_payload.Generate(secondary_target_file)
1741 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001742 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001743
Tianjie Xucfa86222016-03-07 16:31:19 -08001744 # If dm-verity is supported for the device, copy contents of care_map
1745 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001746 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001747 if (target_info.get("verity") == "true" or
1748 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001749 care_map_path = "META/care_map.txt"
1750 namelist = target_zip.namelist()
1751 if care_map_path in namelist:
1752 care_map_data = target_zip.read(care_map_path)
Tao Baoc7b403a2018-01-30 18:19:04 -08001753 # In order to support streaming, care_map.txt needs to be packed as
1754 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001755 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001756 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001757 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001758 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001759
Tao Baobcd1d162017-08-26 13:10:26 -07001760 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001761 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001762
Tao Bao21803d32017-04-19 10:16:09 -07001763 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001764
Tao Baof5110492018-03-02 09:47:43 -08001765 # We haven't written the metadata entry yet, which will be handled in
1766 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001767 common.ZipClose(output_zip)
1768
Tao Baob6304672018-03-08 16:28:33 -08001769 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1770 # all the info of the latter. However, system updaters and OTA servers need to
1771 # take time to switch to the new flag. We keep both of the flags for
1772 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Bao69203522018-03-08 16:09:01 -08001773 needed_property_files = (
Tao Baob6304672018-03-08 16:28:33 -08001774 AbOtaPropertyFiles(),
Tao Bao69203522018-03-08 16:09:01 -08001775 StreamingPropertyFiles(),
1776 )
1777 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001778
Tao Baoc098e9e2016-01-07 13:03:56 -08001779
Doug Zongkereef39442009-04-02 12:14:19 -07001780def main(argv):
1781
1782 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001783 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001784 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001785 elif o in ("-i", "--incremental_from"):
1786 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001787 elif o == "--full_radio":
1788 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001789 elif o == "--full_bootloader":
1790 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001791 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001792 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001793 elif o == "--downgrade":
1794 OPTIONS.downgrade = True
1795 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001796 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001797 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001798 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001799 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001800 elif o == "--oem_no_mount":
1801 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001802 elif o in ("-e", "--extra_script"):
1803 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001804 elif o in ("-t", "--worker_threads"):
1805 if a.isdigit():
1806 OPTIONS.worker_threads = int(a)
1807 else:
1808 raise ValueError("Cannot parse value %r for option %r - only "
1809 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001810 elif o in ("-2", "--two_step"):
1811 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001812 elif o == "--include_secondary":
1813 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001814 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001815 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001816 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001817 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001818 elif o == "--block":
1819 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001820 elif o in ("-b", "--binary"):
1821 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001822 elif o == "--stash_threshold":
1823 try:
1824 OPTIONS.stash_threshold = float(a)
1825 except ValueError:
1826 raise ValueError("Cannot parse value %r for option %r - expecting "
1827 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001828 elif o == "--log_diff":
1829 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001830 elif o == "--payload_signer":
1831 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001832 elif o == "--payload_signer_args":
1833 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001834 elif o == "--extracted_input_target_files":
1835 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001836 elif o == "--skip_postinstall":
1837 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001838 else:
1839 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001840 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001841
1842 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001843 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001844 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001845 "package_key=",
1846 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001847 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001848 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001849 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001850 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001851 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001852 "extra_script=",
1853 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001854 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001855 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001856 "no_signing",
1857 "block",
1858 "binary=",
1859 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001860 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001861 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001862 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001863 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001864 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001865 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001866 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001867 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001868 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001869
1870 if len(args) != 2:
1871 common.Usage(__doc__)
1872 sys.exit(1)
1873
Tao Bao5d182562016-02-23 11:38:39 -08001874 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08001875 # We should only allow downgrading incrementals (as opposed to full).
1876 # Otherwise the device may go back from arbitrary build with this full
1877 # OTA package.
1878 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001879 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001880
Tao Bao2db13852018-01-08 22:28:57 -08001881 # Load the build info dicts from the zip directly or the extracted input
1882 # directory. We don't need to unzip the entire target-files zips, because they
1883 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1884 # When loading the info dicts, we don't need to provide the second parameter
1885 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1886 # some properties with their actual paths, such as 'selinux_fc',
1887 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001888 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001889 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001890 else:
Tao Bao2db13852018-01-08 22:28:57 -08001891 with zipfile.ZipFile(args[0], 'r') as input_zip:
1892 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001893
Tao Bao2db13852018-01-08 22:28:57 -08001894 if OPTIONS.verbose:
1895 print("--- target info ---")
1896 common.DumpInfoDict(OPTIONS.info_dict)
1897
1898 # Load the source build dict if applicable.
1899 if OPTIONS.incremental_source is not None:
1900 OPTIONS.target_info_dict = OPTIONS.info_dict
1901 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1902 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1903
1904 if OPTIONS.verbose:
1905 print("--- source info ---")
1906 common.DumpInfoDict(OPTIONS.source_info_dict)
1907
1908 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001909 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1910
Tao Baoc098e9e2016-01-07 13:03:56 -08001911 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1912
Christian Oderf63e2cd2017-05-01 22:30:15 +02001913 # Use the default key to sign the package if not specified with package_key.
1914 # package_keys are needed on ab_updates, so always define them if an
1915 # ab_update is getting created.
1916 if not OPTIONS.no_signing or ab_update:
1917 if OPTIONS.package_key is None:
1918 OPTIONS.package_key = OPTIONS.info_dict.get(
1919 "default_system_dev_certificate",
1920 "build/target/product/security/testkey")
1921 # Get signing keys
1922 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1923
Tao Baoc098e9e2016-01-07 13:03:56 -08001924 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001925 WriteABOTAPackageWithBrilloScript(
1926 target_file=args[0],
1927 output_file=args[1],
1928 source_file=OPTIONS.incremental_source)
1929
Tao Bao89fbb0f2017-01-10 10:47:58 -08001930 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001931 return
1932
Tao Bao2db13852018-01-08 22:28:57 -08001933 # Sanity check the loaded info dicts first.
1934 if OPTIONS.info_dict.get("no_recovery") == "true":
1935 raise common.ExternalError(
1936 "--- target build has specified no recovery ---")
1937
1938 # Non-A/B OTAs rely on /cache partition to store temporary files.
1939 cache_size = OPTIONS.info_dict.get("cache_size")
1940 if cache_size is None:
1941 print("--- can't determine the cache partition size ---")
1942 OPTIONS.cache_size = cache_size
1943
Doug Zongker1c390a22009-05-14 19:06:36 -07001944 if OPTIONS.extra_script is not None:
1945 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1946
Dan Willemsencea5cd22017-03-21 14:44:27 -07001947 if OPTIONS.extracted_input is not None:
1948 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001949 else:
1950 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001951 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001952 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001953
Tao Bao2db13852018-01-08 22:28:57 -08001954 # If the caller explicitly specified the device-specific extensions path via
1955 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1956 # is present in the target target_files. Otherwise, take the path of the file
1957 # from 'tool_extensions' in the info dict and look for that in the local
1958 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001959 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001960 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1961 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001962 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001963 OPTIONS.device_specific = from_input
1964 else:
Tao Bao2db13852018-01-08 22:28:57 -08001965 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001966
Doug Zongker37974732010-09-16 17:44:38 -07001967 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001968 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001969
Tao Bao767e3ac2015-11-10 12:19:19 -08001970 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001971 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08001972 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Baoc0746f42018-02-21 13:17:22 -08001973 WriteFullOTAPackage(
1974 input_zip,
1975 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08001976
Tao Bao32b80dc2018-01-08 22:50:47 -08001977 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001978 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001979 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001980 OPTIONS.source_tmp = common.UnzipTemp(
1981 OPTIONS.incremental_source, UNZIP_PATTERN)
1982 with zipfile.ZipFile(args[0], 'r') as input_zip, \
1983 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Baoc0746f42018-02-21 13:17:22 -08001984 WriteBlockIncrementalOTAPackage(
1985 input_zip,
1986 source_zip,
1987 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08001988
1989 if OPTIONS.log_diff:
1990 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001991 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001992 target_files_diff.recursiveDiff(
1993 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001994
Tao Bao89fbb0f2017-01-10 10:47:58 -08001995 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001996
1997
1998if __name__ == '__main__':
1999 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002000 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002001 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002002 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002003 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002004 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002005 finally:
2006 common.Cleanup()