blob: daf959f80cde53e6eecac7d1228bf179ce661c9d [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070027 to an older one (e.g. downgrading from P preview back to O MR1).
28 "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29 will always be enforced when using this flag, so "ota-wipe=yes" will also
30 be included in the metadata file. The update-binary in the source build
31 will be used in the OTA package, unless --binary flag is specified. Please
32 also check the comment for --override_timestamp below.
Tao Bao30df8b42018-04-23 15:32:53 -070033
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
41 META/misc_info.txt, or "build/target/product/security/testkey" if that
42 value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070049 to an older one (based on timestamp comparison), by setting the downgrade
50 flag in the package metadata. This differs from --downgrade flag, as we
51 don't enforce a data wipe with this flag. Because we know for sure this is
52 NOT an actual downgrade case, but two builds happen to be cut in a reverse
53 order (e.g. from two branches). A legit use case is that we cut a new
54 build C (after having A and B), but want to enfore an update path of A ->
55 C -> B. Specifying --downgrade may not help since that would enforce a
56 data wipe for C -> B update.
57
58 We used to set a fake timestamp in the package metadata for this flow. But
59 now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60 based on timestamp) with the same "ota-downgrade=yes" flag, with the
61 difference being whether "ota-wipe=yes" is set.
Doug Zongkereef39442009-04-02 12:14:19 -070062
Tao Bao30df8b42018-04-23 15:32:53 -070063 --wipe_user_data
64 Generate an OTA package that will wipe the user data partition when
65 installed.
66
67Non-A/B OTA specific options
68
69 -b (--binary) <file>
70 Use the given binary as the update-binary in the output package, instead
71 of the binary in the build's target_files. Use for development only.
72
73 --block
74 Generate a block-based OTA for non-A/B device. We have deprecated the
75 support for file-based OTA since O. Block-based OTA will be used by
76 default for all non-A/B devices. Keeping this flag here to not break
77 existing callers.
78
79 -e (--extra_script) <file>
80 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070081
leozwangaa6c1a12015-08-14 10:57:58 -070082 --full_bootloader
83 Similar to --full_radio. When generating an incremental OTA, always
84 include a full copy of bootloader image.
85
Tao Bao30df8b42018-04-23 15:32:53 -070086 --full_radio
87 When generating an incremental OTA, always include a full copy of radio
88 image. This option is only meaningful when -i is specified, because a full
89 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -070090
Tao Bao30df8b42018-04-23 15:32:53 -070091 --log_diff <file>
92 Generate a log file that shows the differences in the source and target
93 builds for an incremental package. This option is only meaningful when -i
94 is specified.
95
96 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080097 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -080098 properties on the OEM partition of the intended device. Multiple expected
99 values can be used by providing multiple files. Only the first dict will
100 be used to compute fingerprint, while the rest will be used to assert
101 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800102
Tao Bao8608cde2016-02-25 19:49:55 -0800103 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700104 For devices with OEM-specific properties but without an OEM partition, do
105 not mount the OEM partition in the updater-script. This should be very
106 rarely used, since it's expected to have a dedicated OEM partition for
107 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800108
Tao Bao30df8b42018-04-23 15:32:53 -0700109 --stash_threshold <float>
110 Specify the threshold that will be used to compute the maximum allowed
111 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700112
Tao Bao30df8b42018-04-23 15:32:53 -0700113 -t (--worker_threads) <int>
114 Specify the number of worker-threads that will be used when generating
115 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800116
Tao Bao30df8b42018-04-23 15:32:53 -0700117 --verify
118 Verify the checksums of the updated system and vendor (if any) partitions.
119 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700120
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800121 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700122 Generate a 'two-step' OTA package, where recovery is updated first, so
123 that any changes made to the system partition are done using the new
124 recovery (new kernel, etc.).
125
126A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800127
Tao Baof7140c02018-01-30 17:09:24 -0800128 --include_secondary
129 Additionally include the payload for secondary slot images (default:
130 False). Only meaningful when generating A/B OTAs.
131
132 By default, an A/B OTA package doesn't contain the images for the
133 secondary slot (e.g. system_other.img). Specifying this flag allows
134 generating a separate payload that will install secondary slot images.
135
136 Such a package needs to be applied in a two-stage manner, with a reboot
137 in-between. During the first stage, the updater applies the primary
138 payload only. Upon finishing, it reboots the device into the newly updated
139 slot. It then continues to install the secondary payload to the inactive
140 slot, but without switching the active slot at the end (needs the matching
141 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
142
143 Due to the special install procedure, the secondary payload will be always
144 generated as a full payload.
145
Tao Baodea0f8b2016-06-20 17:55:06 -0700146 --payload_signer <signer>
147 Specify the signer when signing the payload and metadata for A/B OTAs.
148 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
149 with the package private key. If the private key cannot be accessed
150 directly, a payload signer that knows how to do that should be specified.
151 The signer will be supplied with "-inkey <path_to_key>",
152 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700153
154 --payload_signer_args <args>
155 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800156
157 --skip_postinstall
158 Skip the postinstall hooks when generating an A/B OTA package (default:
159 False). Note that this discards ALL the hooks, including non-optional
160 ones. Should only be used if caller knows it's safe to do so (e.g. all the
161 postinstall work is to dexopt apps and a data wipe will happen immediately
162 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700163"""
164
Tao Bao89fbb0f2017-01-10 10:47:58 -0800165from __future__ import print_function
166
Doug Zongkerfc44a512014-08-26 13:10:25 -0700167import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800168import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700169import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800170import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800171import struct
Tao Bao481bab82017-12-21 11:23:09 -0800172import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700173import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700174import zipfile
175
176import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700177import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700178import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700179
Tao Bao481bab82017-12-21 11:23:09 -0800180if sys.hexversion < 0x02070000:
181 print("Python 2.7 or newer is required.", file=sys.stderr)
182 sys.exit(1)
183
184
Doug Zongkereef39442009-04-02 12:14:19 -0700185OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700186OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700187OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700188OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700189OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700190OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800191OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700192OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700193OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
194if OPTIONS.worker_threads == 0:
195 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800196OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800197OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900198OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800199OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800200OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700201OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800202OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700203OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700204OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700205# Stash size cannot exceed cache_size * threshold.
206OPTIONS.cache_size = None
207OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800208OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700209OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700210OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700211OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200212OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800213OPTIONS.skip_postinstall = False
214
Tao Bao8dcf7382015-05-21 14:09:49 -0700215
Tao Bao2dd1c482017-02-03 16:49:39 -0800216METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800217POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800218UNZIP_PATTERN = ['IMAGES/*', 'META/*']
219
Tao Bao2dd1c482017-02-03 16:49:39 -0800220
Tao Bao481bab82017-12-21 11:23:09 -0800221class BuildInfo(object):
222 """A class that holds the information for a given build.
223
224 This class wraps up the property querying for a given source or target build.
225 It abstracts away the logic of handling OEM-specific properties, and caches
226 the commonly used properties such as fingerprint.
227
228 There are two types of info dicts: a) build-time info dict, which is generated
229 at build time (i.e. included in a target_files zip); b) OEM info dict that is
230 specified at package generation time (via command line argument
231 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
232 having "oem_fingerprint_properties" in build-time info dict), all the queries
233 would be answered based on build-time info dict only. Otherwise if using
234 OEM-specific properties, some of them will be calculated from two info dicts.
235
236 Users can query properties similarly as using a dict() (e.g. info['fstab']),
237 or to query build properties via GetBuildProp() or GetVendorBuildProp().
238
239 Attributes:
240 info_dict: The build-time info dict.
241 is_ab: Whether it's a build that uses A/B OTA.
242 oem_dicts: A list of OEM dicts.
243 oem_props: A list of OEM properties that should be read from OEM dicts; None
244 if the build doesn't use any OEM-specific property.
245 fingerprint: The fingerprint of the build, which would be calculated based
246 on OEM properties if applicable.
247 device: The device name, which could come from OEM dicts if applicable.
248 """
249
250 def __init__(self, info_dict, oem_dicts):
251 """Initializes a BuildInfo instance with the given dicts.
252
Tao Bao667c7532018-07-06 10:13:59 -0700253 Note that it only wraps up the given dicts, without making copies.
254
Tao Bao481bab82017-12-21 11:23:09 -0800255 Arguments:
256 info_dict: The build-time info dict.
257 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
258 that it always uses the first dict to calculate the fingerprint or the
259 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700260 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800261 """
262 self.info_dict = info_dict
263 self.oem_dicts = oem_dicts
264
265 self._is_ab = info_dict.get("ab_update") == "true"
266 self._oem_props = info_dict.get("oem_fingerprint_properties")
267
268 if self._oem_props:
269 assert oem_dicts, "OEM source required for this build"
270
271 # These two should be computed only after setting self._oem_props.
272 self._device = self.GetOemProperty("ro.product.device")
273 self._fingerprint = self.CalculateFingerprint()
274
275 @property
276 def is_ab(self):
277 return self._is_ab
278
279 @property
280 def device(self):
281 return self._device
282
283 @property
284 def fingerprint(self):
285 return self._fingerprint
286
287 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700288 def vendor_fingerprint(self):
289 if "vendor.build.prop" not in self.info_dict:
290 return None
291 vendor_build_prop = self.info_dict["vendor.build.prop"]
292 if "ro.vendor.build.fingerprint" in vendor_build_prop:
293 return vendor_build_prop["ro.vendor.build.fingerprint"]
294 if "ro.vendor.build.thumbprint" in vendor_build_prop:
295 return vendor_build_prop["ro.vendor.build.thumbprint"]
296 return None
297
298 @property
Tao Bao481bab82017-12-21 11:23:09 -0800299 def oem_props(self):
300 return self._oem_props
301
302 def __getitem__(self, key):
303 return self.info_dict[key]
304
Tao Bao667c7532018-07-06 10:13:59 -0700305 def __setitem__(self, key, value):
306 self.info_dict[key] = value
307
Tao Bao481bab82017-12-21 11:23:09 -0800308 def get(self, key, default=None):
309 return self.info_dict.get(key, default)
310
Tao Bao667c7532018-07-06 10:13:59 -0700311 def items(self):
312 return self.info_dict.items()
313
Tao Bao481bab82017-12-21 11:23:09 -0800314 def GetBuildProp(self, prop):
315 """Returns the inquired build property."""
316 try:
317 return self.info_dict.get("build.prop", {})[prop]
318 except KeyError:
319 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
320
321 def GetVendorBuildProp(self, prop):
322 """Returns the inquired vendor build property."""
323 try:
324 return self.info_dict.get("vendor.build.prop", {})[prop]
325 except KeyError:
326 raise common.ExternalError(
327 "couldn't find %s in vendor.build.prop" % (prop,))
328
329 def GetOemProperty(self, key):
330 if self.oem_props is not None and key in self.oem_props:
331 return self.oem_dicts[0][key]
332 return self.GetBuildProp(key)
333
334 def CalculateFingerprint(self):
335 if self.oem_props is None:
336 return self.GetBuildProp("ro.build.fingerprint")
337 return "%s/%s/%s:%s" % (
338 self.GetOemProperty("ro.product.brand"),
339 self.GetOemProperty("ro.product.name"),
340 self.GetOemProperty("ro.product.device"),
341 self.GetBuildProp("ro.build.thumbprint"))
342
343 def WriteMountOemScript(self, script):
344 assert self.oem_props is not None
345 recovery_mount_options = self.info_dict.get("recovery_mount_options")
346 script.Mount("/oem", recovery_mount_options)
347
348 def WriteDeviceAssertions(self, script, oem_no_mount):
349 # Read the property directly if not using OEM properties.
350 if not self.oem_props:
351 script.AssertDevice(self.device)
352 return
353
354 # Otherwise assert OEM properties.
355 if not self.oem_dicts:
356 raise common.ExternalError(
357 "No OEM file provided to answer expected assertions")
358
359 for prop in self.oem_props.split():
360 values = []
361 for oem_dict in self.oem_dicts:
362 if prop in oem_dict:
363 values.append(oem_dict[prop])
364 if not values:
365 raise common.ExternalError(
366 "The OEM file is missing the property %s" % (prop,))
367 script.AssertOemProperty(prop, values, oem_no_mount)
368
369
Tao Baofabe0832018-01-17 15:52:28 -0800370class PayloadSigner(object):
371 """A class that wraps the payload signing works.
372
373 When generating a Payload, hashes of the payload and metadata files will be
374 signed with the device key, either by calling an external payload signer or
375 by calling openssl with the package key. This class provides a unified
376 interface, so that callers can just call PayloadSigner.Sign().
377
378 If an external payload signer has been specified (OPTIONS.payload_signer), it
379 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
380 that the signing key should be provided as part of the payload_signer_args.
381 Otherwise without an external signer, it uses the package key
382 (OPTIONS.package_key) and calls openssl for the signing works.
383 """
384
385 def __init__(self):
386 if OPTIONS.payload_signer is None:
387 # Prepare the payload signing key.
388 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
389 pw = OPTIONS.key_passwords[OPTIONS.package_key]
390
391 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
392 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
393 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
394 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700395 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800396
397 self.signer = "openssl"
398 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
399 "-pkeyopt", "digest:sha256"]
400 else:
401 self.signer = OPTIONS.payload_signer
402 self.signer_args = OPTIONS.payload_signer_args
403
404 def Sign(self, in_file):
405 """Signs the given input file. Returns the output filename."""
406 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
407 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700408 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800409 return out_file
410
411
Tao Bao40b18822018-01-30 18:19:04 -0800412class Payload(object):
413 """Manages the creation and the signing of an A/B OTA Payload."""
414
415 PAYLOAD_BIN = 'payload.bin'
416 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800417 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
418 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800419
Tao Bao667ff572018-02-10 00:02:40 -0800420 def __init__(self, secondary=False):
421 """Initializes a Payload instance.
422
423 Args:
424 secondary: Whether it's generating a secondary payload (default: False).
425 """
Tao Bao40b18822018-01-30 18:19:04 -0800426 self.payload_file = None
427 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800428 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800429
430 def Generate(self, target_file, source_file=None, additional_args=None):
431 """Generates a payload from the given target-files zip(s).
432
433 Args:
434 target_file: The filename of the target build target-files zip.
435 source_file: The filename of the source build target-files zip; or None if
436 generating a full OTA.
437 additional_args: A list of additional args that should be passed to
438 brillo_update_payload script; or None.
439 """
440 if additional_args is None:
441 additional_args = []
442
443 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
444 cmd = ["brillo_update_payload", "generate",
445 "--payload", payload_file,
446 "--target_image", target_file]
447 if source_file is not None:
448 cmd.extend(["--source_image", source_file])
449 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700450 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800451
452 self.payload_file = payload_file
453 self.payload_properties = None
454
455 def Sign(self, payload_signer):
456 """Generates and signs the hashes of the payload and metadata.
457
458 Args:
459 payload_signer: A PayloadSigner() instance that serves the signing work.
460
461 Raises:
462 AssertionError: On any failure when calling brillo_update_payload script.
463 """
464 assert isinstance(payload_signer, PayloadSigner)
465
466 # 1. Generate hashes of the payload and metadata files.
467 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
468 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
469 cmd = ["brillo_update_payload", "hash",
470 "--unsigned_payload", self.payload_file,
471 "--signature_size", "256",
472 "--metadata_hash_file", metadata_sig_file,
473 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700474 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800475
476 # 2. Sign the hashes.
477 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
478 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
479
480 # 3. Insert the signatures back into the payload file.
481 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
482 suffix=".bin")
483 cmd = ["brillo_update_payload", "sign",
484 "--unsigned_payload", self.payload_file,
485 "--payload", signed_payload_file,
486 "--signature_size", "256",
487 "--metadata_signature_file", signed_metadata_sig_file,
488 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700489 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800490
491 # 4. Dump the signed payload properties.
492 properties_file = common.MakeTempFile(prefix="payload-properties-",
493 suffix=".txt")
494 cmd = ["brillo_update_payload", "properties",
495 "--payload", signed_payload_file,
496 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700497 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800498
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 Bao40b18822018-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 Bao40b18822018-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 Bao40b18822018-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 Bao40b18822018-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 Bao40b18822018-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
Tao Baobcd1d162017-08-26 13:10:26 -0700692 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800693 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700694 AddCompatibilityArchive(True, True)
695 return
696
Tao Bao481bab82017-12-21 11:23:09 -0800697 source_fp = source_info.fingerprint
698 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700699 system_updated = source_fp != target_fp
700
Tao Baoea6cbd02018-09-05 13:06:37 -0700701 source_fp_vendor = source_info.vendor_fingerprint
702 target_fp_vendor = target_info.vendor_fingerprint
703 # vendor build fingerprints could be possibly blacklisted at build time. For
704 # such a case, we consider the vendor images being changed.
705 if source_fp_vendor is None or target_fp_vendor is None:
706 vendor_updated = True
707 else:
708 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700709
710 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700711
712
Tao Bao491d7e22018-02-21 13:17:22 -0800713def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800714 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700715
Tao Bao481bab82017-12-21 11:23:09 -0800716 # We don't know what version it will be installed on top of. We expect the API
717 # just won't change very often. Similarly for fstab, it might have changed in
718 # the target build.
719 target_api_version = target_info["recovery_api_version"]
720 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700721
Tao Bao481bab82017-12-21 11:23:09 -0800722 if target_info.oem_props and not OPTIONS.oem_no_mount:
723 target_info.WriteMountOemScript(script)
724
Tao Baodf3a48b2018-01-10 16:30:43 -0800725 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700726
Tao Bao491d7e22018-02-21 13:17:22 -0800727 if not OPTIONS.no_signing:
728 staging_file = common.MakeTempFile(suffix='.zip')
729 else:
730 staging_file = output_file
731
732 output_zip = zipfile.ZipFile(
733 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
734
Doug Zongker05d3dea2009-06-22 11:32:31 -0700735 device_specific = common.DeviceSpecificParams(
736 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800737 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700738 output_zip=output_zip,
739 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700740 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700741 metadata=metadata,
742 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700743
Tao Bao457cbf62017-03-06 09:56:01 -0800744 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800745
Tao Bao481bab82017-12-21 11:23:09 -0800746 # Assertions (e.g. downgrade check, device properties check).
747 ts = target_info.GetBuildProp("ro.build.date.utc")
748 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700749 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700750
Tao Bao481bab82017-12-21 11:23:09 -0800751 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700752 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800753
754 # Two-step package strategy (in chronological order, which is *not*
755 # the order in which the generated script has things):
756 #
757 # if stage is not "2/3" or "3/3":
758 # write recovery image to boot partition
759 # set stage to "2/3"
760 # reboot to boot partition and restart recovery
761 # else if stage is "2/3":
762 # write recovery image to recovery partition
763 # set stage to "3/3"
764 # reboot to recovery partition and restart recovery
765 # else:
766 # (stage must be "3/3")
767 # set stage to ""
768 # do normal full package installation:
769 # wipe and install system, boot image, etc.
770 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700771 # complete script normally
772 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800773
774 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
775 OPTIONS.input_tmp, "RECOVERY")
776 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800777 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800778 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800779 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800780 assert fs.fs_type.upper() == "EMMC", \
781 "two-step packages only supported on devices with EMMC /misc partitions"
782 bcb_dev = {"bcb_dev": fs.device}
783 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
784 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700785if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800786""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800787
788 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
789 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800790 script.WriteRawImage("/recovery", "recovery.img")
791 script.AppendExtra("""
792set_stage("%(bcb_dev)s", "3/3");
793reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700794else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800795""" % bcb_dev)
796
Tao Baod42e97e2016-11-30 12:11:57 -0800797 # Stage 3/3: Make changes.
798 script.Comment("Stage 3/3")
799
Tao Bao6c55a8a2015-04-08 15:30:27 -0700800 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800801 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700802
Doug Zongkere5ff5902012-01-17 10:55:37 -0800803 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700804
Doug Zongker01ce19c2014-02-04 13:48:15 -0800805 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700806
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700807 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800808 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700809 if HasVendorPartition(input_zip):
810 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700811
Doug Zongker4b9596f2014-06-09 14:15:45 -0700812 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800813
Tao Baoe709b092018-02-07 12:40:00 -0800814 # See the notes in WriteBlockIncrementalOTAPackage().
815 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
816
Tao Bao457cbf62017-03-06 09:56:01 -0800817 # Full OTA is done as an "incremental" against an empty source image. This
818 # has the effect of writing new data from the package to the entire
819 # partition, but lets us reuse the updater code that writes incrementals to
820 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800821 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
822 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800823 system_tgt.ResetFileMap()
824 system_diff = common.BlockDifference("system", system_tgt, src=None)
Tao Bao76def242017-11-21 09:25:31 -0800825 system_diff.WriteScript(script, output_zip,
826 write_verify_script=OPTIONS.verify)
Doug Zongkereef39442009-04-02 12:14:19 -0700827
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700828 boot_img = common.GetBootableImage(
829 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800830
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700831 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700832 script.ShowProgress(0.1, 0)
833
Tao Baoe709b092018-02-07 12:40:00 -0800834 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
835 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800836 vendor_tgt.ResetFileMap()
837 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Tao Bao76def242017-11-21 09:25:31 -0800838 vendor_diff.WriteScript(script, output_zip,
839 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700840
Tao Bao481bab82017-12-21 11:23:09 -0800841 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700842
Tao Bao481bab82017-12-21 11:23:09 -0800843 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700844 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700845
Doug Zongker01ce19c2014-02-04 13:48:15 -0800846 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700847 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700848
Doug Zongker01ce19c2014-02-04 13:48:15 -0800849 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700850 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700851
Doug Zongker1c390a22009-05-14 19:06:36 -0700852 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700853 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700854
Doug Zongker14833602010-02-02 13:12:04 -0800855 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800856
Doug Zongker922206e2014-03-04 13:16:24 -0800857 if OPTIONS.wipe_user_data:
858 script.ShowProgress(0.1, 10)
859 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700860
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800861 if OPTIONS.two_step:
862 script.AppendExtra("""
863set_stage("%(bcb_dev)s", "");
864""" % bcb_dev)
865 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800866
867 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
868 script.Comment("Stage 1/3")
869 _WriteRecoveryImageToBoot(script, output_zip)
870
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800871 script.AppendExtra("""
872set_stage("%(bcb_dev)s", "2/3");
873reboot_now("%(bcb_dev)s", "");
874endif;
875endif;
876""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800877
Tao Bao5d182562016-02-23 11:38:39 -0800878 script.SetProgress(1)
879 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800880 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800881
882 # We haven't written the metadata entry, which will be done in
883 # FinalizeMetadata.
884 common.ZipClose(output_zip)
885
886 needed_property_files = (
887 NonAbOtaPropertyFiles(),
888 )
889 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700890
Doug Zongkerfc44a512014-08-26 13:10:25 -0700891
Doug Zongker2ea21062010-04-28 16:05:21 -0700892def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800893 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
894 common.ZipWriteStr(output_zip, METADATA_NAME, value,
895 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700896
Doug Zongkerfc44a512014-08-26 13:10:25 -0700897
Tao Bao481bab82017-12-21 11:23:09 -0800898def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800899 # Only incremental OTAs are allowed to reach here.
900 assert OPTIONS.incremental_source is not None
901
Tao Bao481bab82017-12-21 11:23:09 -0800902 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
903 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800904 is_downgrade = long(post_timestamp) < long(pre_timestamp)
905
906 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800907 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700908 raise RuntimeError(
909 "--downgrade or --override_timestamp specified but no downgrade "
910 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800911 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800912 else:
913 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700914 raise RuntimeError(
915 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
916 "Need to specify --override_timestamp OR --downgrade to allow "
917 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800918
919
Tao Baodf3a48b2018-01-10 16:30:43 -0800920def GetPackageMetadata(target_info, source_info=None):
921 """Generates and returns the metadata dict.
922
923 It generates a dict() that contains the info to be written into an OTA
924 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700925 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800926
927 Args:
928 target_info: The BuildInfo instance that holds the target build info.
929 source_info: The BuildInfo instance that holds the source build info, or
930 None if generating full OTA.
931
932 Returns:
933 A dict to be written into package metadata entry.
934 """
935 assert isinstance(target_info, BuildInfo)
936 assert source_info is None or isinstance(source_info, BuildInfo)
937
938 metadata = {
939 'post-build' : target_info.fingerprint,
940 'post-build-incremental' : target_info.GetBuildProp(
941 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800942 'post-sdk-level' : target_info.GetBuildProp(
943 'ro.build.version.sdk'),
944 'post-security-patch-level' : target_info.GetBuildProp(
945 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800946 }
947
948 if target_info.is_ab:
949 metadata['ota-type'] = 'AB'
950 metadata['ota-required-cache'] = '0'
951 else:
952 metadata['ota-type'] = 'BLOCK'
953
954 if OPTIONS.wipe_user_data:
955 metadata['ota-wipe'] = 'yes'
956
957 is_incremental = source_info is not None
958 if is_incremental:
959 metadata['pre-build'] = source_info.fingerprint
960 metadata['pre-build-incremental'] = source_info.GetBuildProp(
961 'ro.build.version.incremental')
962 metadata['pre-device'] = source_info.device
963 else:
964 metadata['pre-device'] = target_info.device
965
Tao Baofaa8e0b2018-04-12 14:31:43 -0700966 # Use the actual post-timestamp, even for a downgrade case.
967 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
968
969 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800970 if is_incremental:
971 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800972
973 return metadata
974
975
Tao Baod3fc38a2018-03-08 16:09:01 -0800976class PropertyFiles(object):
977 """A class that computes the property-files string for an OTA package.
978
979 A property-files string is a comma-separated string that contains the
980 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
981 can be fetched directly with the package URL along with the offset/size info.
982 These strings can be used for streaming A/B OTAs, or allowing an updater to
983 download package metadata entry directly, without paying the cost of
984 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800985
Tao Baocc8e2662018-03-01 19:30:00 -0800986 Computing the final property-files string requires two passes. Because doing
987 the whole package signing (with signapk.jar) will possibly reorder the ZIP
988 entries, which may in turn invalidate earlier computed ZIP entry offset/size
989 values.
990
991 This class provides functions to be called for each pass. The general flow is
992 as follows.
993
Tao Baod3fc38a2018-03-08 16:09:01 -0800994 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800995 # The first pass, which writes placeholders before doing initial signing.
996 property_files.Compute()
997 SignOutput()
998
999 # The second pass, by replacing the placeholders with actual data.
1000 property_files.Finalize()
1001 SignOutput()
1002
1003 And the caller can additionally verify the final result.
1004
1005 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001006 """
1007
Tao Baocc8e2662018-03-01 19:30:00 -08001008 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001009 self.name = None
1010 self.required = ()
1011 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001012
Tao Baocc8e2662018-03-01 19:30:00 -08001013 def Compute(self, input_zip):
1014 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001015
Tao Baocc8e2662018-03-01 19:30:00 -08001016 We reserve extra space for the offset and size of the metadata entry itself,
1017 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001018
Tao Baocc8e2662018-03-01 19:30:00 -08001019 Args:
1020 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001021
Tao Baocc8e2662018-03-01 19:30:00 -08001022 Returns:
1023 A string with placeholders for the metadata offset/size info, e.g.
1024 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1025 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001026 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001027
Tao Baod2ce2ed2018-03-16 12:59:42 -07001028 class InsufficientSpaceException(Exception):
1029 pass
1030
Tao Baocc8e2662018-03-01 19:30:00 -08001031 def Finalize(self, input_zip, reserved_length):
1032 """Finalizes a property-files string with actual METADATA offset/size info.
1033
1034 The input ZIP file has been signed, with the ZIP entries in the desired
1035 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1036 the ZIP entry offsets and construct the property-files string with actual
1037 data. Note that during this process, we must pad the property-files string
1038 to the reserved length, so that the METADATA entry size remains the same.
1039 Otherwise the entries' offsets and sizes may change again.
1040
1041 Args:
1042 input_zip: The input ZIP file.
1043 reserved_length: The reserved length of the property-files string during
1044 the call to Compute(). The final string must be no more than this
1045 size.
1046
1047 Returns:
1048 A property-files string including the metadata offset/size info, e.g.
1049 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1050
1051 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001052 InsufficientSpaceException: If the reserved length is insufficient to hold
1053 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001054 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001055 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001056 if len(result) > reserved_length:
1057 raise self.InsufficientSpaceException(
1058 'Insufficient reserved space: reserved={}, actual={}'.format(
1059 reserved_length, len(result)))
1060
Tao Baocc8e2662018-03-01 19:30:00 -08001061 result += ' ' * (reserved_length - len(result))
1062 return result
1063
1064 def Verify(self, input_zip, expected):
1065 """Verifies the input ZIP file contains the expected property-files string.
1066
1067 Args:
1068 input_zip: The input ZIP file.
1069 expected: The property-files string that's computed from Finalize().
1070
1071 Raises:
1072 AssertionError: On finding a mismatch.
1073 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001074 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001075 assert actual == expected, \
1076 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1077
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001078 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1079 """
1080 Constructs the property-files string per request.
1081
1082 Args:
1083 zip_file: The input ZIP file.
1084 reserved_length: The reserved length of the property-files string.
1085
1086 Returns:
1087 A property-files string including the metadata offset/size info, e.g.
1088 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1089 """
Tao Baocc8e2662018-03-01 19:30:00 -08001090
1091 def ComputeEntryOffsetSize(name):
1092 """Computes the zip entry offset and size."""
1093 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001094 offset = info.header_offset
1095 offset += zipfile.sizeFileHeader
1096 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001097 size = info.file_size
1098 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1099
1100 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001101 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001102 for entry in self.required:
1103 tokens.append(ComputeEntryOffsetSize(entry))
1104 for entry in self.optional:
1105 if entry in zip_file.namelist():
1106 tokens.append(ComputeEntryOffsetSize(entry))
1107
1108 # 'META-INF/com/android/metadata' is required. We don't know its actual
1109 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001110 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1111 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1112 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1113 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001114 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001115 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001116 else:
1117 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1118
1119 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001120
Tao Bao85f16982018-03-08 16:28:33 -08001121 def _GetPrecomputed(self, input_zip):
1122 """Computes the additional tokens to be included into the property-files.
1123
1124 This applies to tokens without actual ZIP entries, such as
1125 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1126 that they can download the payload metadata directly with the info.
1127
1128 Args:
1129 input_zip: The input zip file.
1130
1131 Returns:
1132 A list of strings (tokens) to be added to the property-files string.
1133 """
1134 # pylint: disable=no-self-use
1135 # pylint: disable=unused-argument
1136 return []
1137
Tao Baofe5b69a2018-03-02 09:47:43 -08001138
Tao Baod3fc38a2018-03-08 16:09:01 -08001139class StreamingPropertyFiles(PropertyFiles):
1140 """A subclass for computing the property-files for streaming A/B OTAs."""
1141
1142 def __init__(self):
1143 super(StreamingPropertyFiles, self).__init__()
1144 self.name = 'ota-streaming-property-files'
1145 self.required = (
1146 # payload.bin and payload_properties.txt must exist.
1147 'payload.bin',
1148 'payload_properties.txt',
1149 )
1150 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001151 # care_map is available only if dm-verity is enabled.
1152 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001153 'care_map.txt',
1154 # compatibility.zip is available only if target supports Treble.
1155 'compatibility.zip',
1156 )
1157
1158
Tao Bao85f16982018-03-08 16:28:33 -08001159class AbOtaPropertyFiles(StreamingPropertyFiles):
1160 """The property-files for A/B OTA that includes payload_metadata.bin info.
1161
1162 Since P, we expose one more token (aka property-file), in addition to the ones
1163 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1164 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1165 doesn't exist as a separate ZIP entry, but can be used to verify if the
1166 payload can be applied on the given device.
1167
1168 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1169 and the newly added 'ota-property-files' in P. The new token will only be
1170 available in 'ota-property-files'.
1171 """
1172
1173 def __init__(self):
1174 super(AbOtaPropertyFiles, self).__init__()
1175 self.name = 'ota-property-files'
1176
1177 def _GetPrecomputed(self, input_zip):
1178 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1179 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1180
1181 @staticmethod
1182 def _GetPayloadMetadataOffsetAndSize(input_zip):
1183 """Computes the offset and size of the payload metadata for a given package.
1184
1185 (From system/update_engine/update_metadata.proto)
1186 A delta update file contains all the deltas needed to update a system from
1187 one specific version to another specific version. The update format is
1188 represented by this struct pseudocode:
1189
1190 struct delta_update_file {
1191 char magic[4] = "CrAU";
1192 uint64 file_format_version;
1193 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1194
1195 // Only present if format_version > 1:
1196 uint32 metadata_signature_size;
1197
1198 // The Bzip2 compressed DeltaArchiveManifest
1199 char manifest[metadata_signature_size];
1200
1201 // The signature of the metadata (from the beginning of the payload up to
1202 // this location, not including the signature itself). This is a
1203 // serialized Signatures message.
1204 char medatada_signature_message[metadata_signature_size];
1205
1206 // Data blobs for files, no specific format. The specific offset
1207 // and length of each data blob is recorded in the DeltaArchiveManifest.
1208 struct {
1209 char data[];
1210 } blobs[];
1211
1212 // These two are not signed:
1213 uint64 payload_signatures_message_size;
1214 char payload_signatures_message[];
1215 };
1216
1217 'payload-metadata.bin' contains all the bytes from the beginning of the
1218 payload, till the end of 'medatada_signature_message'.
1219 """
1220 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001221 payload_offset = payload_info.header_offset
1222 payload_offset += zipfile.sizeFileHeader
1223 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001224 payload_size = payload_info.file_size
1225
1226 with input_zip.open('payload.bin', 'r') as payload_fp:
1227 header_bin = payload_fp.read(24)
1228
1229 # network byte order (big-endian)
1230 header = struct.unpack("!IQQL", header_bin)
1231
1232 # 'CrAU'
1233 magic = header[0]
1234 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1235
1236 manifest_size = header[2]
1237 metadata_signature_size = header[3]
1238 metadata_total = 24 + manifest_size + metadata_signature_size
1239 assert metadata_total < payload_size
1240
1241 return (payload_offset, metadata_total)
1242
1243
Tao Bao491d7e22018-02-21 13:17:22 -08001244class NonAbOtaPropertyFiles(PropertyFiles):
1245 """The property-files for non-A/B OTA.
1246
1247 For non-A/B OTA, the property-files string contains the info for METADATA
1248 entry, with which a system updater can be fetched the package metadata prior
1249 to downloading the entire package.
1250 """
1251
1252 def __init__(self):
1253 super(NonAbOtaPropertyFiles, self).__init__()
1254 self.name = 'ota-property-files'
1255
1256
Tao Baod3fc38a2018-03-08 16:09:01 -08001257def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001258 """Finalizes the metadata and signs an A/B OTA package.
1259
1260 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1261 that contains the offsets and sizes for the ZIP entries. An example
1262 property-files string is as follows.
1263
1264 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1265
1266 OTA server can pass down this string, in addition to the package URL, to the
1267 system update client. System update client can then fetch individual ZIP
1268 entries (ZIP_STORED) directly at the given offset of the URL.
1269
1270 Args:
1271 metadata: The metadata dict for the package.
1272 input_file: The input ZIP filename that doesn't contain the package METADATA
1273 entry yet.
1274 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001275 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001276 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001277
Tao Baod2ce2ed2018-03-16 12:59:42 -07001278 def ComputeAllPropertyFiles(input_file, needed_property_files):
1279 # Write the current metadata entry with placeholders.
1280 with zipfile.ZipFile(input_file) as input_zip:
1281 for property_files in needed_property_files:
1282 metadata[property_files.name] = property_files.Compute(input_zip)
1283 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001284
Tao Baod2ce2ed2018-03-16 12:59:42 -07001285 if METADATA_NAME in namelist:
1286 common.ZipDelete(input_file, METADATA_NAME)
1287 output_zip = zipfile.ZipFile(input_file, 'a')
1288 WriteMetadata(metadata, output_zip)
1289 common.ZipClose(output_zip)
1290
1291 if OPTIONS.no_signing:
1292 return input_file
1293
Tao Bao491d7e22018-02-21 13:17:22 -08001294 prelim_signing = common.MakeTempFile(suffix='.zip')
1295 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001296 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001297
Tao Baod2ce2ed2018-03-16 12:59:42 -07001298 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1299 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1300 for property_files in needed_property_files:
1301 metadata[property_files.name] = property_files.Finalize(
1302 prelim_signing_zip, len(metadata[property_files.name]))
1303
1304 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1305 # entries, as well as padding the entry headers. We do a preliminary signing
1306 # (with an incomplete metadata entry) to allow that to happen. Then compute
1307 # the ZIP entry offsets, write back the final metadata and do the final
1308 # signing.
1309 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1310 try:
1311 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1312 except PropertyFiles.InsufficientSpaceException:
1313 # Even with the preliminary signing, the entry orders may change
1314 # dramatically, which leads to insufficiently reserved space during the
1315 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1316 # preliminary signing works, based on the already ordered ZIP entries, to
1317 # address the issue.
1318 prelim_signing = ComputeAllPropertyFiles(
1319 prelim_signing, needed_property_files)
1320 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001321
1322 # Replace the METADATA entry.
1323 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001324 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001325 WriteMetadata(metadata, output_zip)
1326 common.ZipClose(output_zip)
1327
1328 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001329 if OPTIONS.no_signing:
1330 output_file = prelim_signing
1331 else:
1332 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001333
1334 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001335 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001336 for property_files in needed_property_files:
1337 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001338
1339
Tao Bao491d7e22018-02-21 13:17:22 -08001340def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001341 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1342 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001343
Tao Bao481bab82017-12-21 11:23:09 -08001344 target_api_version = target_info["recovery_api_version"]
1345 source_api_version = source_info["recovery_api_version"]
1346 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001347 print("WARNING: generating edify script for a source that "
1348 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001349
Tao Bao481bab82017-12-21 11:23:09 -08001350 script = edify_generator.EdifyGenerator(
1351 source_api_version, target_info, fstab=source_info["fstab"])
1352
1353 if target_info.oem_props or source_info.oem_props:
1354 if not OPTIONS.oem_no_mount:
1355 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001356
Tao Baodf3a48b2018-01-10 16:30:43 -08001357 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001358
Tao Bao491d7e22018-02-21 13:17:22 -08001359 if not OPTIONS.no_signing:
1360 staging_file = common.MakeTempFile(suffix='.zip')
1361 else:
1362 staging_file = output_file
1363
1364 output_zip = zipfile.ZipFile(
1365 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1366
Geremy Condra36bd3652014-02-06 19:45:10 -08001367 device_specific = common.DeviceSpecificParams(
1368 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001369 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001370 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001371 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001372 output_zip=output_zip,
1373 script=script,
1374 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001375 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001376
Geremy Condra36bd3652014-02-06 19:45:10 -08001377 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001378 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001379 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001380 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001381 updating_boot = (not OPTIONS.two_step and
1382 (source_boot.data != target_boot.data))
1383
Geremy Condra36bd3652014-02-06 19:45:10 -08001384 target_recovery = common.GetBootableImage(
1385 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001386
Tao Baoe709b092018-02-07 12:40:00 -08001387 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1388 # shared blocks (i.e. some blocks will show up in multiple files' block
1389 # list). We can only allocate such shared blocks to the first "owner", and
1390 # disable imgdiff for all later occurrences.
1391 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1392 target_info.get('ext4_share_dup_blocks') == "true")
1393 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1394 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001395
1396 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1397 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001398 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001399 allow_shared_blocks,
1400 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001401
Tao Bao0582cb62017-12-21 11:47:01 -08001402 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001403 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001404 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001405
Tao Baof8acad12016-07-07 09:09:58 -07001406 # Check the first block of the source system partition for remount R/W only
1407 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001408 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001409 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001410 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1411 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1412 # b) the blocks listed in block map may not contain all the bytes for a given
1413 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001414 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001415 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1416 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001417 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001418 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001419 version=blockimgdiff_version,
1420 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001421
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001422 if HasVendorPartition(target_zip):
1423 if not HasVendorPartition(source_zip):
1424 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001425 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1426 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001427 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1428 "vendor", 4096, target_info)
1429 vendor_tgt = common.GetSparseImage(
1430 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1431 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001432
1433 # Check first block of vendor partition for remount R/W only if
1434 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001435 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001436 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001437 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001438 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001439 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001440 version=blockimgdiff_version,
1441 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001442 else:
1443 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001444
Tao Baobcd1d162017-08-26 13:10:26 -07001445 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001446 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001447
Tao Bao481bab82017-12-21 11:23:09 -08001448 # Assertions (e.g. device properties check).
1449 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001450 device_specific.IncrementalOTA_Assertions()
1451
1452 # Two-step incremental package strategy (in chronological order,
1453 # which is *not* the order in which the generated script has
1454 # things):
1455 #
1456 # if stage is not "2/3" or "3/3":
1457 # do verification on current system
1458 # write recovery image to boot partition
1459 # set stage to "2/3"
1460 # reboot to boot partition and restart recovery
1461 # else if stage is "2/3":
1462 # write recovery image to recovery partition
1463 # set stage to "3/3"
1464 # reboot to recovery partition and restart recovery
1465 # else:
1466 # (stage must be "3/3")
1467 # perform update:
1468 # patch system files, etc.
1469 # force full install of new boot image
1470 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001471 # complete script normally
1472 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001473
1474 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001475 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001476 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001477 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001478 assert fs.fs_type.upper() == "EMMC", \
1479 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001480 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001481 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1482 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001483if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001484""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001485
1486 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1487 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001488 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001489 script.WriteRawImage("/recovery", "recovery.img")
1490 script.AppendExtra("""
1491set_stage("%(bcb_dev)s", "3/3");
1492reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001493else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001494""" % bcb_dev)
1495
Tao Baod42e97e2016-11-30 12:11:57 -08001496 # Stage 1/3: (a) Verify the current system.
1497 script.Comment("Stage 1/3")
1498
Tao Bao6c55a8a2015-04-08 15:30:27 -07001499 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001500 script.Print("Source: {}".format(source_info.fingerprint))
1501 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001502
Geremy Condra36bd3652014-02-06 19:45:10 -08001503 script.Print("Verifying current system...")
1504
1505 device_specific.IncrementalOTA_VerifyBegin()
1506
Tao Bao481bab82017-12-21 11:23:09 -08001507 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001508
Tao Baod8d14be2016-02-04 14:26:02 -08001509 # Check the required cache size (i.e. stashed blocks).
1510 size = []
1511 if system_diff:
1512 size.append(system_diff.required_cache)
1513 if vendor_diff:
1514 size.append(vendor_diff.required_cache)
1515
Geremy Condra36bd3652014-02-06 19:45:10 -08001516 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001517 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001518 d = common.Difference(target_boot, source_boot)
1519 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001520 if d is None:
1521 include_full_boot = True
1522 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1523 else:
1524 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001525
Tao Bao89fbb0f2017-01-10 10:47:58 -08001526 print("boot target: %d source: %d diff: %d" % (
1527 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001528
Tao Bao51216552018-08-26 11:53:15 -07001529 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001530
Tao Bao51216552018-08-26 11:53:15 -07001531 script.PatchPartitionCheck(
1532 "{}:{}:{}:{}".format(
1533 boot_type, boot_device, target_boot.size, target_boot.sha1),
1534 "{}:{}:{}:{}".format(
1535 boot_type, boot_device, source_boot.size, source_boot.sha1))
1536
Tao Baod8d14be2016-02-04 14:26:02 -08001537 size.append(target_boot.size)
1538
1539 if size:
1540 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001541
1542 device_specific.IncrementalOTA_VerifyEnd()
1543
1544 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001545 # Stage 1/3: (b) Write recovery image to /boot.
1546 _WriteRecoveryImageToBoot(script, output_zip)
1547
Geremy Condra36bd3652014-02-06 19:45:10 -08001548 script.AppendExtra("""
1549set_stage("%(bcb_dev)s", "2/3");
1550reboot_now("%(bcb_dev)s", "");
1551else
1552""" % bcb_dev)
1553
Tao Baod42e97e2016-11-30 12:11:57 -08001554 # Stage 3/3: Make changes.
1555 script.Comment("Stage 3/3")
1556
Jesse Zhao75bcea02015-01-06 10:59:53 -08001557 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001558 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001559 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001560 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001561
Geremy Condra36bd3652014-02-06 19:45:10 -08001562 script.Comment("---- start making changes here ----")
1563
1564 device_specific.IncrementalOTA_InstallBegin()
1565
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001566 system_diff.WriteScript(script, output_zip,
Tao Bao76def242017-11-21 09:25:31 -08001567 progress=0.8 if vendor_diff else 0.9,
1568 write_verify_script=OPTIONS.verify)
Tao Bao68658c02015-06-01 13:40:49 -07001569
Doug Zongkerfc44a512014-08-26 13:10:25 -07001570 if vendor_diff:
Tao Bao76def242017-11-21 09:25:31 -08001571 vendor_diff.WriteScript(script, output_zip, progress=0.1,
1572 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001573
1574 if OPTIONS.two_step:
1575 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1576 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001577 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001578
1579 if not OPTIONS.two_step:
1580 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001581 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001582 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001583 script.Print("Installing boot image...")
1584 script.WriteRawImage("/boot", "boot.img")
1585 else:
1586 # Produce the boot image by applying a patch to the current
1587 # contents of the boot partition, and write it back to the
1588 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001589 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001590 script.Print("Patching boot image...")
1591 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001592 script.PatchPartition(
1593 '{}:{}:{}:{}'.format(
1594 boot_type, boot_device, target_boot.size, target_boot.sha1),
1595 '{}:{}:{}:{}'.format(
1596 boot_type, boot_device, source_boot.size, source_boot.sha1),
1597 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001598 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001599 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001600
1601 # Do device-specific installation (eg, write radio image).
1602 device_specific.IncrementalOTA_InstallEnd()
1603
1604 if OPTIONS.extra_script is not None:
1605 script.AppendExtra(OPTIONS.extra_script)
1606
Doug Zongker922206e2014-03-04 13:16:24 -08001607 if OPTIONS.wipe_user_data:
1608 script.Print("Erasing user data...")
1609 script.FormatPartition("/data")
1610
Geremy Condra36bd3652014-02-06 19:45:10 -08001611 if OPTIONS.two_step:
1612 script.AppendExtra("""
1613set_stage("%(bcb_dev)s", "");
1614endif;
1615endif;
1616""" % bcb_dev)
1617
1618 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001619 # For downgrade OTAs, we prefer to use the update-binary in the source
1620 # build that is actually newer than the one in the target build.
1621 if OPTIONS.downgrade:
1622 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1623 else:
1624 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001625 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001626
1627 # We haven't written the metadata entry yet, which will be handled in
1628 # FinalizeMetadata().
1629 common.ZipClose(output_zip)
1630
1631 # Sign the generated zip package unless no_signing is specified.
1632 needed_property_files = (
1633 NonAbOtaPropertyFiles(),
1634 )
1635 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001636
Doug Zongker32b527d2014-03-04 10:03:02 -08001637
Tao Bao15a146a2018-02-21 16:06:59 -08001638def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001639 """Returns a target-files.zip file for generating secondary payload.
1640
1641 Although the original target-files.zip already contains secondary slot
1642 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1643 ones without _other suffix. Note that we cannot instead modify the names in
1644 META/ab_partitions.txt, because there are no matching partitions on device.
1645
1646 For the partitions that don't have secondary images, the ones for primary
1647 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1648 bootloader images in the inactive slot.
1649
1650 Args:
1651 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001652 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001653
1654 Returns:
1655 The filename of the target-files.zip for generating secondary payload.
1656 """
1657 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1658 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1659
Tao Baodba59ee2018-01-09 13:21:02 -08001660 with zipfile.ZipFile(input_file, 'r') as input_zip:
1661 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001662 namelist = input_zip.namelist()
1663
1664 # Additionally unzip 'RADIO/*' if exists.
1665 unzip_pattern = UNZIP_PATTERN[:]
1666 if any([entry.startswith('RADIO/') for entry in namelist]):
1667 unzip_pattern.append('RADIO/*')
1668 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001669
1670 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001671 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1672 if info.filename == 'IMAGES/system_other.img':
1673 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1674
1675 # Primary images and friends need to be skipped explicitly.
1676 elif info.filename in ('IMAGES/system.img',
1677 'IMAGES/system.map'):
1678 pass
1679
Tao Bao15a146a2018-02-21 16:06:59 -08001680 # Skip copying the postinstall config if requested.
1681 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1682 pass
1683
Tao Bao12489802018-07-12 14:47:38 -07001684 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001685 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1686
Tao Baof7140c02018-01-30 17:09:24 -08001687 common.ZipClose(target_zip)
1688
1689 return target_file
1690
1691
Tao Bao15a146a2018-02-21 16:06:59 -08001692def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1693 """Returns a target-files.zip that's not containing postinstall_config.txt.
1694
1695 This allows brillo_update_payload script to skip writing all the postinstall
1696 hooks in the generated payload. The input target-files.zip file will be
1697 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1698 contain the postinstall_config.txt entry, the input file will be returned.
1699
1700 Args:
1701 input_file: The input target-files.zip filename.
1702
1703 Returns:
1704 The filename of target-files.zip that doesn't contain postinstall config.
1705 """
1706 # We should only make a copy if postinstall_config entry exists.
1707 with zipfile.ZipFile(input_file, 'r') as input_zip:
1708 if POSTINSTALL_CONFIG not in input_zip.namelist():
1709 return input_file
1710
1711 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1712 shutil.copyfile(input_file, target_file)
1713 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1714 return target_file
1715
1716
Tao Baoc098e9e2016-01-07 13:03:56 -08001717def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1718 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001719 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001720 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001721 if not OPTIONS.no_signing:
1722 staging_file = common.MakeTempFile(suffix='.zip')
1723 else:
1724 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001725 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001726 compression=zipfile.ZIP_DEFLATED)
1727
Tao Bao481bab82017-12-21 11:23:09 -08001728 if source_file is not None:
1729 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1730 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1731 else:
1732 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1733 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001734
Tao Bao481bab82017-12-21 11:23:09 -08001735 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001736 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001737
Tao Bao15a146a2018-02-21 16:06:59 -08001738 if OPTIONS.skip_postinstall:
1739 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1740
Tao Bao40b18822018-01-30 18:19:04 -08001741 # Generate payload.
1742 payload = Payload()
1743
1744 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001745 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001746 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001747 else:
1748 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001749 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001750
Tao Bao40b18822018-01-30 18:19:04 -08001751 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001752
Tao Bao40b18822018-01-30 18:19:04 -08001753 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001754 payload_signer = PayloadSigner()
1755 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001756
Tao Bao40b18822018-01-30 18:19:04 -08001757 # Write the payload into output zip.
1758 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001759
Tao Baof7140c02018-01-30 17:09:24 -08001760 # Generate and include the secondary payload that installs secondary images
1761 # (e.g. system_other.img).
1762 if OPTIONS.include_secondary:
1763 # We always include a full payload for the secondary slot, even when
1764 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001765 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1766 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001767 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001768 secondary_payload.Generate(secondary_target_file,
1769 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001770 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001771 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001772
Tianjie Xucfa86222016-03-07 16:31:19 -08001773 # If dm-verity is supported for the device, copy contents of care_map
1774 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001775 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001776 if (target_info.get("verity") == "true" or
1777 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001778 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1779 "META/" + x in target_zip.namelist()]
1780
1781 # Adds care_map if either the protobuf format or the plain text one exists.
1782 if care_map_list:
1783 care_map_name = care_map_list[0]
1784 care_map_data = target_zip.read("META/" + care_map_name)
1785 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001786 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001787 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001788 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001789 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001790 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001791
Tao Baobcd1d162017-08-26 13:10:26 -07001792 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001793 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001794
Tao Bao21803d32017-04-19 10:16:09 -07001795 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001796
Tao Baofe5b69a2018-03-02 09:47:43 -08001797 # We haven't written the metadata entry yet, which will be handled in
1798 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001799 common.ZipClose(output_zip)
1800
Tao Bao85f16982018-03-08 16:28:33 -08001801 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1802 # all the info of the latter. However, system updaters and OTA servers need to
1803 # take time to switch to the new flag. We keep both of the flags for
1804 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001805 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001806 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001807 StreamingPropertyFiles(),
1808 )
1809 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001810
Tao Baoc098e9e2016-01-07 13:03:56 -08001811
Doug Zongkereef39442009-04-02 12:14:19 -07001812def main(argv):
1813
1814 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001815 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001816 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001817 elif o in ("-i", "--incremental_from"):
1818 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001819 elif o == "--full_radio":
1820 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001821 elif o == "--full_bootloader":
1822 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001823 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001824 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001825 elif o == "--downgrade":
1826 OPTIONS.downgrade = True
1827 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001828 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001829 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001830 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001831 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001832 elif o == "--oem_no_mount":
1833 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001834 elif o in ("-e", "--extra_script"):
1835 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001836 elif o in ("-t", "--worker_threads"):
1837 if a.isdigit():
1838 OPTIONS.worker_threads = int(a)
1839 else:
1840 raise ValueError("Cannot parse value %r for option %r - only "
1841 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001842 elif o in ("-2", "--two_step"):
1843 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001844 elif o == "--include_secondary":
1845 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001846 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001847 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001848 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001849 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001850 elif o == "--block":
1851 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001852 elif o in ("-b", "--binary"):
1853 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001854 elif o == "--stash_threshold":
1855 try:
1856 OPTIONS.stash_threshold = float(a)
1857 except ValueError:
1858 raise ValueError("Cannot parse value %r for option %r - expecting "
1859 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001860 elif o == "--log_diff":
1861 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001862 elif o == "--payload_signer":
1863 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001864 elif o == "--payload_signer_args":
1865 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001866 elif o == "--extracted_input_target_files":
1867 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001868 elif o == "--skip_postinstall":
1869 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001870 else:
1871 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001872 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001873
1874 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001875 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001876 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001877 "package_key=",
1878 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001879 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001880 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001881 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001882 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001883 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001884 "extra_script=",
1885 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001886 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001887 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001888 "no_signing",
1889 "block",
1890 "binary=",
1891 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001892 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001893 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001894 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001895 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001896 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001897 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001898 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001899 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001900 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001901
1902 if len(args) != 2:
1903 common.Usage(__doc__)
1904 sys.exit(1)
1905
Tao Bao5d182562016-02-23 11:38:39 -08001906 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08001907 # We should only allow downgrading incrementals (as opposed to full).
1908 # Otherwise the device may go back from arbitrary build with this full
1909 # OTA package.
1910 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001911 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001912
Tao Bao2db13852018-01-08 22:28:57 -08001913 # Load the build info dicts from the zip directly or the extracted input
1914 # directory. We don't need to unzip the entire target-files zips, because they
1915 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1916 # When loading the info dicts, we don't need to provide the second parameter
1917 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1918 # some properties with their actual paths, such as 'selinux_fc',
1919 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001920 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001921 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001922 else:
Tao Bao2db13852018-01-08 22:28:57 -08001923 with zipfile.ZipFile(args[0], 'r') as input_zip:
1924 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001925
Tao Bao2db13852018-01-08 22:28:57 -08001926 if OPTIONS.verbose:
1927 print("--- target info ---")
1928 common.DumpInfoDict(OPTIONS.info_dict)
1929
1930 # Load the source build dict if applicable.
1931 if OPTIONS.incremental_source is not None:
1932 OPTIONS.target_info_dict = OPTIONS.info_dict
1933 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1934 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1935
1936 if OPTIONS.verbose:
1937 print("--- source info ---")
1938 common.DumpInfoDict(OPTIONS.source_info_dict)
1939
1940 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001941 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1942
Tao Baoc098e9e2016-01-07 13:03:56 -08001943 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1944
Christian Oderf63e2cd2017-05-01 22:30:15 +02001945 # Use the default key to sign the package if not specified with package_key.
1946 # package_keys are needed on ab_updates, so always define them if an
1947 # ab_update is getting created.
1948 if not OPTIONS.no_signing or ab_update:
1949 if OPTIONS.package_key is None:
1950 OPTIONS.package_key = OPTIONS.info_dict.get(
1951 "default_system_dev_certificate",
1952 "build/target/product/security/testkey")
1953 # Get signing keys
1954 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1955
Tao Baoc098e9e2016-01-07 13:03:56 -08001956 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001957 WriteABOTAPackageWithBrilloScript(
1958 target_file=args[0],
1959 output_file=args[1],
1960 source_file=OPTIONS.incremental_source)
1961
Tao Bao89fbb0f2017-01-10 10:47:58 -08001962 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001963 return
1964
Tao Bao2db13852018-01-08 22:28:57 -08001965 # Sanity check the loaded info dicts first.
1966 if OPTIONS.info_dict.get("no_recovery") == "true":
1967 raise common.ExternalError(
1968 "--- target build has specified no recovery ---")
1969
1970 # Non-A/B OTAs rely on /cache partition to store temporary files.
1971 cache_size = OPTIONS.info_dict.get("cache_size")
1972 if cache_size is None:
1973 print("--- can't determine the cache partition size ---")
1974 OPTIONS.cache_size = cache_size
1975
Doug Zongker1c390a22009-05-14 19:06:36 -07001976 if OPTIONS.extra_script is not None:
1977 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1978
Dan Willemsencea5cd22017-03-21 14:44:27 -07001979 if OPTIONS.extracted_input is not None:
1980 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001981 else:
1982 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001983 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001984 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001985
Tao Bao2db13852018-01-08 22:28:57 -08001986 # If the caller explicitly specified the device-specific extensions path via
1987 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1988 # is present in the target target_files. Otherwise, take the path of the file
1989 # from 'tool_extensions' in the info dict and look for that in the local
1990 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001991 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001992 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1993 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001994 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001995 OPTIONS.device_specific = from_input
1996 else:
Tao Bao2db13852018-01-08 22:28:57 -08001997 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001998
Doug Zongker37974732010-09-16 17:44:38 -07001999 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002000 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002001
Tao Bao767e3ac2015-11-10 12:19:19 -08002002 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002003 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002004 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002005 WriteFullOTAPackage(
2006 input_zip,
2007 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002008
Tao Bao32b80dc2018-01-08 22:50:47 -08002009 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002010 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002011 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002012 OPTIONS.source_tmp = common.UnzipTemp(
2013 OPTIONS.incremental_source, UNZIP_PATTERN)
2014 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2015 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002016 WriteBlockIncrementalOTAPackage(
2017 input_zip,
2018 source_zip,
2019 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002020
2021 if OPTIONS.log_diff:
2022 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002023 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002024 target_files_diff.recursiveDiff(
2025 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002026
Tao Bao89fbb0f2017-01-10 10:47:58 -08002027 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002028
2029
2030if __name__ == '__main__':
2031 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002032 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002033 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002034 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002035 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002036 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002037 finally:
2038 common.Cleanup()