blob: 7ea53f8067ca18e3151d7c6d1a86dced73306354 [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 subprocess
173import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700174import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700175import zipfile
176
177import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700178import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700179import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700180
Tao Bao481bab82017-12-21 11:23:09 -0800181if sys.hexversion < 0x02070000:
182 print("Python 2.7 or newer is required.", file=sys.stderr)
183 sys.exit(1)
184
185
Doug Zongkereef39442009-04-02 12:14:19 -0700186OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700187OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700188OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700189OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700190OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700191OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800192OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700193OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700194OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
195if OPTIONS.worker_threads == 0:
196 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800197OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800198OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900199OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800200OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800201OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700202OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800203OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700204OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700205OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700206# Stash size cannot exceed cache_size * threshold.
207OPTIONS.cache_size = None
208OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800209OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700210OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700211OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700212OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200213OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800214OPTIONS.skip_postinstall = False
215
Tao Bao8dcf7382015-05-21 14:09:49 -0700216
Tao Bao2dd1c482017-02-03 16:49:39 -0800217METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800218POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800219UNZIP_PATTERN = ['IMAGES/*', 'META/*']
220
Tao Bao2dd1c482017-02-03 16:49:39 -0800221
Tao Bao481bab82017-12-21 11:23:09 -0800222class BuildInfo(object):
223 """A class that holds the information for a given build.
224
225 This class wraps up the property querying for a given source or target build.
226 It abstracts away the logic of handling OEM-specific properties, and caches
227 the commonly used properties such as fingerprint.
228
229 There are two types of info dicts: a) build-time info dict, which is generated
230 at build time (i.e. included in a target_files zip); b) OEM info dict that is
231 specified at package generation time (via command line argument
232 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
233 having "oem_fingerprint_properties" in build-time info dict), all the queries
234 would be answered based on build-time info dict only. Otherwise if using
235 OEM-specific properties, some of them will be calculated from two info dicts.
236
237 Users can query properties similarly as using a dict() (e.g. info['fstab']),
238 or to query build properties via GetBuildProp() or GetVendorBuildProp().
239
240 Attributes:
241 info_dict: The build-time info dict.
242 is_ab: Whether it's a build that uses A/B OTA.
243 oem_dicts: A list of OEM dicts.
244 oem_props: A list of OEM properties that should be read from OEM dicts; None
245 if the build doesn't use any OEM-specific property.
246 fingerprint: The fingerprint of the build, which would be calculated based
247 on OEM properties if applicable.
248 device: The device name, which could come from OEM dicts if applicable.
249 """
250
251 def __init__(self, info_dict, oem_dicts):
252 """Initializes a BuildInfo instance with the given dicts.
253
Tao Bao667c7532018-07-06 10:13:59 -0700254 Note that it only wraps up the given dicts, without making copies.
255
Tao Bao481bab82017-12-21 11:23:09 -0800256 Arguments:
257 info_dict: The build-time info dict.
258 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
259 that it always uses the first dict to calculate the fingerprint or the
260 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700261 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800262 """
263 self.info_dict = info_dict
264 self.oem_dicts = oem_dicts
265
266 self._is_ab = info_dict.get("ab_update") == "true"
267 self._oem_props = info_dict.get("oem_fingerprint_properties")
268
269 if self._oem_props:
270 assert oem_dicts, "OEM source required for this build"
271
272 # These two should be computed only after setting self._oem_props.
273 self._device = self.GetOemProperty("ro.product.device")
274 self._fingerprint = self.CalculateFingerprint()
275
276 @property
277 def is_ab(self):
278 return self._is_ab
279
280 @property
281 def device(self):
282 return self._device
283
284 @property
285 def fingerprint(self):
286 return self._fingerprint
287
288 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700289 def vendor_fingerprint(self):
290 if "vendor.build.prop" not in self.info_dict:
291 return None
292 vendor_build_prop = self.info_dict["vendor.build.prop"]
293 if "ro.vendor.build.fingerprint" in vendor_build_prop:
294 return vendor_build_prop["ro.vendor.build.fingerprint"]
295 if "ro.vendor.build.thumbprint" in vendor_build_prop:
296 return vendor_build_prop["ro.vendor.build.thumbprint"]
297 return None
298
299 @property
Tao Bao481bab82017-12-21 11:23:09 -0800300 def oem_props(self):
301 return self._oem_props
302
303 def __getitem__(self, key):
304 return self.info_dict[key]
305
Tao Bao667c7532018-07-06 10:13:59 -0700306 def __setitem__(self, key, value):
307 self.info_dict[key] = value
308
Tao Bao481bab82017-12-21 11:23:09 -0800309 def get(self, key, default=None):
310 return self.info_dict.get(key, default)
311
Tao Bao667c7532018-07-06 10:13:59 -0700312 def items(self):
313 return self.info_dict.items()
314
Tao Bao481bab82017-12-21 11:23:09 -0800315 def GetBuildProp(self, prop):
316 """Returns the inquired build property."""
317 try:
318 return self.info_dict.get("build.prop", {})[prop]
319 except KeyError:
320 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
321
322 def GetVendorBuildProp(self, prop):
323 """Returns the inquired vendor build property."""
324 try:
325 return self.info_dict.get("vendor.build.prop", {})[prop]
326 except KeyError:
327 raise common.ExternalError(
328 "couldn't find %s in vendor.build.prop" % (prop,))
329
330 def GetOemProperty(self, key):
331 if self.oem_props is not None and key in self.oem_props:
332 return self.oem_dicts[0][key]
333 return self.GetBuildProp(key)
334
335 def CalculateFingerprint(self):
336 if self.oem_props is None:
337 return self.GetBuildProp("ro.build.fingerprint")
338 return "%s/%s/%s:%s" % (
339 self.GetOemProperty("ro.product.brand"),
340 self.GetOemProperty("ro.product.name"),
341 self.GetOemProperty("ro.product.device"),
342 self.GetBuildProp("ro.build.thumbprint"))
343
344 def WriteMountOemScript(self, script):
345 assert self.oem_props is not None
346 recovery_mount_options = self.info_dict.get("recovery_mount_options")
347 script.Mount("/oem", recovery_mount_options)
348
349 def WriteDeviceAssertions(self, script, oem_no_mount):
350 # Read the property directly if not using OEM properties.
351 if not self.oem_props:
352 script.AssertDevice(self.device)
353 return
354
355 # Otherwise assert OEM properties.
356 if not self.oem_dicts:
357 raise common.ExternalError(
358 "No OEM file provided to answer expected assertions")
359
360 for prop in self.oem_props.split():
361 values = []
362 for oem_dict in self.oem_dicts:
363 if prop in oem_dict:
364 values.append(oem_dict[prop])
365 if not values:
366 raise common.ExternalError(
367 "The OEM file is missing the property %s" % (prop,))
368 script.AssertOemProperty(prop, values, oem_no_mount)
369
370
Tao Baofabe0832018-01-17 15:52:28 -0800371class PayloadSigner(object):
372 """A class that wraps the payload signing works.
373
374 When generating a Payload, hashes of the payload and metadata files will be
375 signed with the device key, either by calling an external payload signer or
376 by calling openssl with the package key. This class provides a unified
377 interface, so that callers can just call PayloadSigner.Sign().
378
379 If an external payload signer has been specified (OPTIONS.payload_signer), it
380 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
381 that the signing key should be provided as part of the payload_signer_args.
382 Otherwise without an external signer, it uses the package key
383 (OPTIONS.package_key) and calls openssl for the signing works.
384 """
385
386 def __init__(self):
387 if OPTIONS.payload_signer is None:
388 # Prepare the payload signing key.
389 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
390 pw = OPTIONS.key_passwords[OPTIONS.package_key]
391
392 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
393 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
394 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
395 cmd.extend(["-out", signing_key])
396
Tao Bao73dd4f42018-10-04 16:25:33 -0700397 get_signing_key = common.Run(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800398 stdoutdata, _ = get_signing_key.communicate()
399 assert get_signing_key.returncode == 0, \
400 "Failed to get signing key: {}".format(stdoutdata)
401
402 self.signer = "openssl"
403 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
404 "-pkeyopt", "digest:sha256"]
405 else:
406 self.signer = OPTIONS.payload_signer
407 self.signer_args = OPTIONS.payload_signer_args
408
409 def Sign(self, in_file):
410 """Signs the given input file. Returns the output filename."""
411 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
412 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Bao73dd4f42018-10-04 16:25:33 -0700413 signing = common.Run(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800414 stdoutdata, _ = signing.communicate()
415 assert signing.returncode == 0, \
416 "Failed to sign the input file: {}".format(stdoutdata)
417 return out_file
418
419
Tao Bao40b18822018-01-30 18:19:04 -0800420class Payload(object):
421 """Manages the creation and the signing of an A/B OTA Payload."""
422
423 PAYLOAD_BIN = 'payload.bin'
424 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800425 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
426 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800427
Tao Bao667ff572018-02-10 00:02:40 -0800428 def __init__(self, secondary=False):
429 """Initializes a Payload instance.
430
431 Args:
432 secondary: Whether it's generating a secondary payload (default: False).
433 """
Tao Bao40b18822018-01-30 18:19:04 -0800434 # The place where the output from the subprocess should go.
435 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
436 self.payload_file = None
437 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800438 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800439
440 def Generate(self, target_file, source_file=None, additional_args=None):
441 """Generates a payload from the given target-files zip(s).
442
443 Args:
444 target_file: The filename of the target build target-files zip.
445 source_file: The filename of the source build target-files zip; or None if
446 generating a full OTA.
447 additional_args: A list of additional args that should be passed to
448 brillo_update_payload script; or None.
449 """
450 if additional_args is None:
451 additional_args = []
452
453 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
454 cmd = ["brillo_update_payload", "generate",
455 "--payload", payload_file,
456 "--target_image", target_file]
457 if source_file is not None:
458 cmd.extend(["--source_image", source_file])
459 cmd.extend(additional_args)
460 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
461 stdoutdata, _ = p.communicate()
462 assert p.returncode == 0, \
463 "brillo_update_payload generate failed: {}".format(stdoutdata)
464
465 self.payload_file = payload_file
466 self.payload_properties = None
467
468 def Sign(self, payload_signer):
469 """Generates and signs the hashes of the payload and metadata.
470
471 Args:
472 payload_signer: A PayloadSigner() instance that serves the signing work.
473
474 Raises:
475 AssertionError: On any failure when calling brillo_update_payload script.
476 """
477 assert isinstance(payload_signer, PayloadSigner)
478
479 # 1. Generate hashes of the payload and metadata files.
480 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
481 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
482 cmd = ["brillo_update_payload", "hash",
483 "--unsigned_payload", self.payload_file,
484 "--signature_size", "256",
485 "--metadata_hash_file", metadata_sig_file,
486 "--payload_hash_file", payload_sig_file]
487 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
488 p1.communicate()
489 assert p1.returncode == 0, "brillo_update_payload hash failed"
490
491 # 2. Sign the hashes.
492 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
493 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
494
495 # 3. Insert the signatures back into the payload file.
496 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
497 suffix=".bin")
498 cmd = ["brillo_update_payload", "sign",
499 "--unsigned_payload", self.payload_file,
500 "--payload", signed_payload_file,
501 "--signature_size", "256",
502 "--metadata_signature_file", signed_metadata_sig_file,
503 "--payload_signature_file", signed_payload_sig_file]
504 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
505 p1.communicate()
506 assert p1.returncode == 0, "brillo_update_payload sign failed"
507
508 # 4. Dump the signed payload properties.
509 properties_file = common.MakeTempFile(prefix="payload-properties-",
510 suffix=".txt")
511 cmd = ["brillo_update_payload", "properties",
512 "--payload", signed_payload_file,
513 "--properties_file", properties_file]
514 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
515 p1.communicate()
516 assert p1.returncode == 0, "brillo_update_payload properties failed"
517
Tao Bao667ff572018-02-10 00:02:40 -0800518 if self.secondary:
519 with open(properties_file, "a") as f:
520 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
521
Tao Bao40b18822018-01-30 18:19:04 -0800522 if OPTIONS.wipe_user_data:
523 with open(properties_file, "a") as f:
524 f.write("POWERWASH=1\n")
525
526 self.payload_file = signed_payload_file
527 self.payload_properties = properties_file
528
Tao Bao667ff572018-02-10 00:02:40 -0800529 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800530 """Writes the payload to the given zip.
531
532 Args:
533 output_zip: The output ZipFile instance.
534 """
535 assert self.payload_file is not None
536 assert self.payload_properties is not None
537
Tao Bao667ff572018-02-10 00:02:40 -0800538 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800539 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
540 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
541 else:
542 payload_arcname = Payload.PAYLOAD_BIN
543 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
544
Tao Bao40b18822018-01-30 18:19:04 -0800545 # Add the signed payload file and properties into the zip. In order to
546 # support streaming, we pack them as ZIP_STORED. So these entries can be
547 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800548 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800549 compress_type=zipfile.ZIP_STORED)
550 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800551 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800552 compress_type=zipfile.ZIP_STORED)
553
554
Doug Zongkereef39442009-04-02 12:14:19 -0700555def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200556 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700557
Doug Zongker951495f2009-08-14 12:44:19 -0700558 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
559 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700560
561
Tao Bao481bab82017-12-21 11:23:09 -0800562def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800563 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800564 if not oem_source:
565 return None
566
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800567 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800568 for oem_file in oem_source:
569 with open(oem_file) as fp:
570 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800571 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700572
Doug Zongkereef39442009-04-02 12:14:19 -0700573
Tao Baod42e97e2016-11-30 12:11:57 -0800574def _WriteRecoveryImageToBoot(script, output_zip):
575 """Find and write recovery image to /boot in two-step OTA.
576
577 In two-step OTAs, we write recovery image to /boot as the first step so that
578 we can reboot to there and install a new recovery image to /recovery.
579 A special "recovery-two-step.img" will be preferred, which encodes the correct
580 path of "/boot". Otherwise the device may show "device is corrupt" message
581 when booting into /boot.
582
583 Fall back to using the regular recovery.img if the two-step recovery image
584 doesn't exist. Note that rebuilding the special image at this point may be
585 infeasible, because we don't have the desired boot signer and keys when
586 calling ota_from_target_files.py.
587 """
588
589 recovery_two_step_img_name = "recovery-two-step.img"
590 recovery_two_step_img_path = os.path.join(
591 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
592 if os.path.exists(recovery_two_step_img_path):
593 recovery_two_step_img = common.GetBootableImage(
594 recovery_two_step_img_name, recovery_two_step_img_name,
595 OPTIONS.input_tmp, "RECOVERY")
596 common.ZipWriteStr(
597 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800598 print("two-step package: using %s in stage 1/3" % (
599 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800600 script.WriteRawImage("/boot", recovery_two_step_img_name)
601 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800602 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800603 # The "recovery.img" entry has been written into package earlier.
604 script.WriteRawImage("/boot", "recovery.img")
605
606
Doug Zongkerc9253822014-02-04 12:17:58 -0800607def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700608 namelist = [name for name in target_files_zip.namelist()]
609 return ("SYSTEM/recovery-from-boot.p" in namelist or
610 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700611
Tao Bao457cbf62017-03-06 09:56:01 -0800612
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700613def HasVendorPartition(target_files_zip):
614 try:
615 target_files_zip.getinfo("VENDOR/")
616 return True
617 except KeyError:
618 return False
619
Tao Bao457cbf62017-03-06 09:56:01 -0800620
Tao Bao481bab82017-12-21 11:23:09 -0800621def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700622 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800623 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700624
625
Tao Bao481bab82017-12-21 11:23:09 -0800626def WriteFingerprintAssertion(script, target_info, source_info):
627 source_oem_props = source_info.oem_props
628 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700629
Tao Bao481bab82017-12-21 11:23:09 -0800630 if source_oem_props is None and target_oem_props is None:
631 script.AssertSomeFingerprint(
632 source_info.fingerprint, target_info.fingerprint)
633 elif source_oem_props is not None and target_oem_props is not None:
634 script.AssertSomeThumbprint(
635 target_info.GetBuildProp("ro.build.thumbprint"),
636 source_info.GetBuildProp("ro.build.thumbprint"))
637 elif source_oem_props is None and target_oem_props is not None:
638 script.AssertFingerprintOrThumbprint(
639 source_info.fingerprint,
640 target_info.GetBuildProp("ro.build.thumbprint"))
641 else:
642 script.AssertFingerprintOrThumbprint(
643 target_info.fingerprint,
644 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700645
Doug Zongkerfc44a512014-08-26 13:10:25 -0700646
Tao Bao481bab82017-12-21 11:23:09 -0800647def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
648 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700649 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700650
651 Metadata used for on-device compatibility verification is retrieved from
652 target_zip then added to compatibility.zip which is added to the output_zip
653 archive.
654
Tao Baobcd1d162017-08-26 13:10:26 -0700655 Compatibility archive should only be included for devices that have enabled
656 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700657
658 Args:
659 target_zip: Zip file containing the source files to be included for OTA.
660 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800661 target_info: The BuildInfo instance that holds the target build info.
662 source_info: The BuildInfo instance that holds the source build info, if
663 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700664 """
665
Tao Baobcd1d162017-08-26 13:10:26 -0700666 def AddCompatibilityArchive(system_updated, vendor_updated):
667 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700668
Tao Baobcd1d162017-08-26 13:10:26 -0700669 Args:
670 system_updated: If True, the system image will be updated and therefore
671 its metadata should be included.
672 vendor_updated: If True, the vendor image will be updated and therefore
673 its metadata should be included.
674 """
675 # Determine what metadata we need. Files are names relative to META/.
676 compatibility_files = []
677 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
678 system_metadata = ("system_manifest.xml", "system_matrix.xml")
679 if vendor_updated:
680 compatibility_files += vendor_metadata
681 if system_updated:
682 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700683
Tao Baobcd1d162017-08-26 13:10:26 -0700684 # Create new archive.
685 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800686 compatibility_archive_zip = zipfile.ZipFile(
687 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700688
Tao Baobcd1d162017-08-26 13:10:26 -0700689 # Add metadata.
690 for file_name in compatibility_files:
691 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700692
Tao Baobcd1d162017-08-26 13:10:26 -0700693 if target_file_name in target_zip.namelist():
694 data = target_zip.read(target_file_name)
695 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700696
Tao Baobcd1d162017-08-26 13:10:26 -0700697 # Ensure files are written before we copy into output_zip.
698 compatibility_archive_zip.close()
699
700 # Only add the archive if we have any compatibility info.
701 if compatibility_archive_zip.namelist():
702 common.ZipWrite(output_zip, compatibility_archive.name,
703 arcname="compatibility.zip",
704 compress_type=zipfile.ZIP_STORED)
705
706 # Will only proceed if the target has enabled the Treble support (as well as
707 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800708 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700709 return
710
Tao Baobcd1d162017-08-26 13:10:26 -0700711 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800712 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700713 AddCompatibilityArchive(True, True)
714 return
715
Tao Bao481bab82017-12-21 11:23:09 -0800716 source_fp = source_info.fingerprint
717 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700718 system_updated = source_fp != target_fp
719
Tao Baoea6cbd02018-09-05 13:06:37 -0700720 source_fp_vendor = source_info.vendor_fingerprint
721 target_fp_vendor = target_info.vendor_fingerprint
722 # vendor build fingerprints could be possibly blacklisted at build time. For
723 # such a case, we consider the vendor images being changed.
724 if source_fp_vendor is None or target_fp_vendor is None:
725 vendor_updated = True
726 else:
727 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700728
729 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700730
731
Tao Bao491d7e22018-02-21 13:17:22 -0800732def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800733 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700734
Tao Bao481bab82017-12-21 11:23:09 -0800735 # We don't know what version it will be installed on top of. We expect the API
736 # just won't change very often. Similarly for fstab, it might have changed in
737 # the target build.
738 target_api_version = target_info["recovery_api_version"]
739 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700740
Tao Bao481bab82017-12-21 11:23:09 -0800741 if target_info.oem_props and not OPTIONS.oem_no_mount:
742 target_info.WriteMountOemScript(script)
743
Tao Baodf3a48b2018-01-10 16:30:43 -0800744 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700745
Tao Bao491d7e22018-02-21 13:17:22 -0800746 if not OPTIONS.no_signing:
747 staging_file = common.MakeTempFile(suffix='.zip')
748 else:
749 staging_file = output_file
750
751 output_zip = zipfile.ZipFile(
752 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
753
Doug Zongker05d3dea2009-06-22 11:32:31 -0700754 device_specific = common.DeviceSpecificParams(
755 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800756 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700757 output_zip=output_zip,
758 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700759 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700760 metadata=metadata,
761 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700762
Tao Bao457cbf62017-03-06 09:56:01 -0800763 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800764
Tao Bao481bab82017-12-21 11:23:09 -0800765 # Assertions (e.g. downgrade check, device properties check).
766 ts = target_info.GetBuildProp("ro.build.date.utc")
767 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700768 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700769
Tao Bao481bab82017-12-21 11:23:09 -0800770 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700771 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800772
773 # Two-step package strategy (in chronological order, which is *not*
774 # the order in which the generated script has things):
775 #
776 # if stage is not "2/3" or "3/3":
777 # write recovery image to boot partition
778 # set stage to "2/3"
779 # reboot to boot partition and restart recovery
780 # else if stage is "2/3":
781 # write recovery image to recovery partition
782 # set stage to "3/3"
783 # reboot to recovery partition and restart recovery
784 # else:
785 # (stage must be "3/3")
786 # set stage to ""
787 # do normal full package installation:
788 # wipe and install system, boot image, etc.
789 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700790 # complete script normally
791 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800792
793 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
794 OPTIONS.input_tmp, "RECOVERY")
795 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800796 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800797 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800798 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800799 assert fs.fs_type.upper() == "EMMC", \
800 "two-step packages only supported on devices with EMMC /misc partitions"
801 bcb_dev = {"bcb_dev": fs.device}
802 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
803 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700804if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800805""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800806
807 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
808 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800809 script.WriteRawImage("/recovery", "recovery.img")
810 script.AppendExtra("""
811set_stage("%(bcb_dev)s", "3/3");
812reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700813else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800814""" % bcb_dev)
815
Tao Baod42e97e2016-11-30 12:11:57 -0800816 # Stage 3/3: Make changes.
817 script.Comment("Stage 3/3")
818
Tao Bao6c55a8a2015-04-08 15:30:27 -0700819 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800820 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700821
Doug Zongkere5ff5902012-01-17 10:55:37 -0800822 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700823
Doug Zongker01ce19c2014-02-04 13:48:15 -0800824 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700825
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700826 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800827 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700828 if HasVendorPartition(input_zip):
829 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700830
Doug Zongker4b9596f2014-06-09 14:15:45 -0700831 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800832
Tao Baoe709b092018-02-07 12:40:00 -0800833 # See the notes in WriteBlockIncrementalOTAPackage().
834 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
835
Tao Bao457cbf62017-03-06 09:56:01 -0800836 # Full OTA is done as an "incremental" against an empty source image. This
837 # has the effect of writing new data from the package to the entire
838 # partition, but lets us reuse the updater code that writes incrementals to
839 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800840 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
841 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800842 system_tgt.ResetFileMap()
843 system_diff = common.BlockDifference("system", system_tgt, src=None)
Tao Bao76def242017-11-21 09:25:31 -0800844 system_diff.WriteScript(script, output_zip,
845 write_verify_script=OPTIONS.verify)
Doug Zongkereef39442009-04-02 12:14:19 -0700846
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700847 boot_img = common.GetBootableImage(
848 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800849
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700850 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700851 script.ShowProgress(0.1, 0)
852
Tao Baoe709b092018-02-07 12:40:00 -0800853 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
854 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800855 vendor_tgt.ResetFileMap()
856 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Tao Bao76def242017-11-21 09:25:31 -0800857 vendor_diff.WriteScript(script, output_zip,
858 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700859
Tao Bao481bab82017-12-21 11:23:09 -0800860 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700861
Tao Bao481bab82017-12-21 11:23:09 -0800862 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700863 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700864
Doug Zongker01ce19c2014-02-04 13:48:15 -0800865 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700866 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700867
Doug Zongker01ce19c2014-02-04 13:48:15 -0800868 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700869 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700870
Doug Zongker1c390a22009-05-14 19:06:36 -0700871 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700872 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700873
Doug Zongker14833602010-02-02 13:12:04 -0800874 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800875
Doug Zongker922206e2014-03-04 13:16:24 -0800876 if OPTIONS.wipe_user_data:
877 script.ShowProgress(0.1, 10)
878 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700879
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800880 if OPTIONS.two_step:
881 script.AppendExtra("""
882set_stage("%(bcb_dev)s", "");
883""" % bcb_dev)
884 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800885
886 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
887 script.Comment("Stage 1/3")
888 _WriteRecoveryImageToBoot(script, output_zip)
889
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800890 script.AppendExtra("""
891set_stage("%(bcb_dev)s", "2/3");
892reboot_now("%(bcb_dev)s", "");
893endif;
894endif;
895""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800896
Tao Bao5d182562016-02-23 11:38:39 -0800897 script.SetProgress(1)
898 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800899 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800900
901 # We haven't written the metadata entry, which will be done in
902 # FinalizeMetadata.
903 common.ZipClose(output_zip)
904
905 needed_property_files = (
906 NonAbOtaPropertyFiles(),
907 )
908 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700909
Doug Zongkerfc44a512014-08-26 13:10:25 -0700910
Doug Zongker2ea21062010-04-28 16:05:21 -0700911def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800912 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
913 common.ZipWriteStr(output_zip, METADATA_NAME, value,
914 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700915
Doug Zongkerfc44a512014-08-26 13:10:25 -0700916
Tao Bao481bab82017-12-21 11:23:09 -0800917def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800918 # Only incremental OTAs are allowed to reach here.
919 assert OPTIONS.incremental_source is not None
920
Tao Bao481bab82017-12-21 11:23:09 -0800921 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
922 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800923 is_downgrade = long(post_timestamp) < long(pre_timestamp)
924
925 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800926 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700927 raise RuntimeError(
928 "--downgrade or --override_timestamp specified but no downgrade "
929 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800930 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800931 else:
932 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700933 raise RuntimeError(
934 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
935 "Need to specify --override_timestamp OR --downgrade to allow "
936 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800937
938
Tao Baodf3a48b2018-01-10 16:30:43 -0800939def GetPackageMetadata(target_info, source_info=None):
940 """Generates and returns the metadata dict.
941
942 It generates a dict() that contains the info to be written into an OTA
943 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700944 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800945
946 Args:
947 target_info: The BuildInfo instance that holds the target build info.
948 source_info: The BuildInfo instance that holds the source build info, or
949 None if generating full OTA.
950
951 Returns:
952 A dict to be written into package metadata entry.
953 """
954 assert isinstance(target_info, BuildInfo)
955 assert source_info is None or isinstance(source_info, BuildInfo)
956
957 metadata = {
958 'post-build' : target_info.fingerprint,
959 'post-build-incremental' : target_info.GetBuildProp(
960 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800961 'post-sdk-level' : target_info.GetBuildProp(
962 'ro.build.version.sdk'),
963 'post-security-patch-level' : target_info.GetBuildProp(
964 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800965 }
966
967 if target_info.is_ab:
968 metadata['ota-type'] = 'AB'
969 metadata['ota-required-cache'] = '0'
970 else:
971 metadata['ota-type'] = 'BLOCK'
972
973 if OPTIONS.wipe_user_data:
974 metadata['ota-wipe'] = 'yes'
975
976 is_incremental = source_info is not None
977 if is_incremental:
978 metadata['pre-build'] = source_info.fingerprint
979 metadata['pre-build-incremental'] = source_info.GetBuildProp(
980 'ro.build.version.incremental')
981 metadata['pre-device'] = source_info.device
982 else:
983 metadata['pre-device'] = target_info.device
984
Tao Baofaa8e0b2018-04-12 14:31:43 -0700985 # Use the actual post-timestamp, even for a downgrade case.
986 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
987
988 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800989 if is_incremental:
990 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800991
992 return metadata
993
994
Tao Baod3fc38a2018-03-08 16:09:01 -0800995class PropertyFiles(object):
996 """A class that computes the property-files string for an OTA package.
997
998 A property-files string is a comma-separated string that contains the
999 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1000 can be fetched directly with the package URL along with the offset/size info.
1001 These strings can be used for streaming A/B OTAs, or allowing an updater to
1002 download package metadata entry directly, without paying the cost of
1003 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001004
Tao Baocc8e2662018-03-01 19:30:00 -08001005 Computing the final property-files string requires two passes. Because doing
1006 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1007 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1008 values.
1009
1010 This class provides functions to be called for each pass. The general flow is
1011 as follows.
1012
Tao Baod3fc38a2018-03-08 16:09:01 -08001013 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001014 # The first pass, which writes placeholders before doing initial signing.
1015 property_files.Compute()
1016 SignOutput()
1017
1018 # The second pass, by replacing the placeholders with actual data.
1019 property_files.Finalize()
1020 SignOutput()
1021
1022 And the caller can additionally verify the final result.
1023
1024 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001025 """
1026
Tao Baocc8e2662018-03-01 19:30:00 -08001027 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001028 self.name = None
1029 self.required = ()
1030 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001031
Tao Baocc8e2662018-03-01 19:30:00 -08001032 def Compute(self, input_zip):
1033 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001034
Tao Baocc8e2662018-03-01 19:30:00 -08001035 We reserve extra space for the offset and size of the metadata entry itself,
1036 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001037
Tao Baocc8e2662018-03-01 19:30:00 -08001038 Args:
1039 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001040
Tao Baocc8e2662018-03-01 19:30:00 -08001041 Returns:
1042 A string with placeholders for the metadata offset/size info, e.g.
1043 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1044 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001045 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001046
Tao Baod2ce2ed2018-03-16 12:59:42 -07001047 class InsufficientSpaceException(Exception):
1048 pass
1049
Tao Baocc8e2662018-03-01 19:30:00 -08001050 def Finalize(self, input_zip, reserved_length):
1051 """Finalizes a property-files string with actual METADATA offset/size info.
1052
1053 The input ZIP file has been signed, with the ZIP entries in the desired
1054 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1055 the ZIP entry offsets and construct the property-files string with actual
1056 data. Note that during this process, we must pad the property-files string
1057 to the reserved length, so that the METADATA entry size remains the same.
1058 Otherwise the entries' offsets and sizes may change again.
1059
1060 Args:
1061 input_zip: The input ZIP file.
1062 reserved_length: The reserved length of the property-files string during
1063 the call to Compute(). The final string must be no more than this
1064 size.
1065
1066 Returns:
1067 A property-files string including the metadata offset/size info, e.g.
1068 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1069
1070 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001071 InsufficientSpaceException: If the reserved length is insufficient to hold
1072 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001073 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001074 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001075 if len(result) > reserved_length:
1076 raise self.InsufficientSpaceException(
1077 'Insufficient reserved space: reserved={}, actual={}'.format(
1078 reserved_length, len(result)))
1079
Tao Baocc8e2662018-03-01 19:30:00 -08001080 result += ' ' * (reserved_length - len(result))
1081 return result
1082
1083 def Verify(self, input_zip, expected):
1084 """Verifies the input ZIP file contains the expected property-files string.
1085
1086 Args:
1087 input_zip: The input ZIP file.
1088 expected: The property-files string that's computed from Finalize().
1089
1090 Raises:
1091 AssertionError: On finding a mismatch.
1092 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001093 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001094 assert actual == expected, \
1095 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1096
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001097 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1098 """
1099 Constructs the property-files string per request.
1100
1101 Args:
1102 zip_file: The input ZIP file.
1103 reserved_length: The reserved length of the property-files string.
1104
1105 Returns:
1106 A property-files string including the metadata offset/size info, e.g.
1107 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1108 """
Tao Baocc8e2662018-03-01 19:30:00 -08001109
1110 def ComputeEntryOffsetSize(name):
1111 """Computes the zip entry offset and size."""
1112 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001113 offset = info.header_offset
1114 offset += zipfile.sizeFileHeader
1115 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001116 size = info.file_size
1117 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1118
1119 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001120 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001121 for entry in self.required:
1122 tokens.append(ComputeEntryOffsetSize(entry))
1123 for entry in self.optional:
1124 if entry in zip_file.namelist():
1125 tokens.append(ComputeEntryOffsetSize(entry))
1126
1127 # 'META-INF/com/android/metadata' is required. We don't know its actual
1128 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001129 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1130 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1131 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1132 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001133 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001134 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001135 else:
1136 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1137
1138 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001139
Tao Bao85f16982018-03-08 16:28:33 -08001140 def _GetPrecomputed(self, input_zip):
1141 """Computes the additional tokens to be included into the property-files.
1142
1143 This applies to tokens without actual ZIP entries, such as
1144 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1145 that they can download the payload metadata directly with the info.
1146
1147 Args:
1148 input_zip: The input zip file.
1149
1150 Returns:
1151 A list of strings (tokens) to be added to the property-files string.
1152 """
1153 # pylint: disable=no-self-use
1154 # pylint: disable=unused-argument
1155 return []
1156
Tao Baofe5b69a2018-03-02 09:47:43 -08001157
Tao Baod3fc38a2018-03-08 16:09:01 -08001158class StreamingPropertyFiles(PropertyFiles):
1159 """A subclass for computing the property-files for streaming A/B OTAs."""
1160
1161 def __init__(self):
1162 super(StreamingPropertyFiles, self).__init__()
1163 self.name = 'ota-streaming-property-files'
1164 self.required = (
1165 # payload.bin and payload_properties.txt must exist.
1166 'payload.bin',
1167 'payload_properties.txt',
1168 )
1169 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001170 # care_map is available only if dm-verity is enabled.
1171 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001172 'care_map.txt',
1173 # compatibility.zip is available only if target supports Treble.
1174 'compatibility.zip',
1175 )
1176
1177
Tao Bao85f16982018-03-08 16:28:33 -08001178class AbOtaPropertyFiles(StreamingPropertyFiles):
1179 """The property-files for A/B OTA that includes payload_metadata.bin info.
1180
1181 Since P, we expose one more token (aka property-file), in addition to the ones
1182 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1183 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1184 doesn't exist as a separate ZIP entry, but can be used to verify if the
1185 payload can be applied on the given device.
1186
1187 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1188 and the newly added 'ota-property-files' in P. The new token will only be
1189 available in 'ota-property-files'.
1190 """
1191
1192 def __init__(self):
1193 super(AbOtaPropertyFiles, self).__init__()
1194 self.name = 'ota-property-files'
1195
1196 def _GetPrecomputed(self, input_zip):
1197 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1198 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1199
1200 @staticmethod
1201 def _GetPayloadMetadataOffsetAndSize(input_zip):
1202 """Computes the offset and size of the payload metadata for a given package.
1203
1204 (From system/update_engine/update_metadata.proto)
1205 A delta update file contains all the deltas needed to update a system from
1206 one specific version to another specific version. The update format is
1207 represented by this struct pseudocode:
1208
1209 struct delta_update_file {
1210 char magic[4] = "CrAU";
1211 uint64 file_format_version;
1212 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1213
1214 // Only present if format_version > 1:
1215 uint32 metadata_signature_size;
1216
1217 // The Bzip2 compressed DeltaArchiveManifest
1218 char manifest[metadata_signature_size];
1219
1220 // The signature of the metadata (from the beginning of the payload up to
1221 // this location, not including the signature itself). This is a
1222 // serialized Signatures message.
1223 char medatada_signature_message[metadata_signature_size];
1224
1225 // Data blobs for files, no specific format. The specific offset
1226 // and length of each data blob is recorded in the DeltaArchiveManifest.
1227 struct {
1228 char data[];
1229 } blobs[];
1230
1231 // These two are not signed:
1232 uint64 payload_signatures_message_size;
1233 char payload_signatures_message[];
1234 };
1235
1236 'payload-metadata.bin' contains all the bytes from the beginning of the
1237 payload, till the end of 'medatada_signature_message'.
1238 """
1239 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001240 payload_offset = payload_info.header_offset
1241 payload_offset += zipfile.sizeFileHeader
1242 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001243 payload_size = payload_info.file_size
1244
1245 with input_zip.open('payload.bin', 'r') as payload_fp:
1246 header_bin = payload_fp.read(24)
1247
1248 # network byte order (big-endian)
1249 header = struct.unpack("!IQQL", header_bin)
1250
1251 # 'CrAU'
1252 magic = header[0]
1253 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1254
1255 manifest_size = header[2]
1256 metadata_signature_size = header[3]
1257 metadata_total = 24 + manifest_size + metadata_signature_size
1258 assert metadata_total < payload_size
1259
1260 return (payload_offset, metadata_total)
1261
1262
Tao Bao491d7e22018-02-21 13:17:22 -08001263class NonAbOtaPropertyFiles(PropertyFiles):
1264 """The property-files for non-A/B OTA.
1265
1266 For non-A/B OTA, the property-files string contains the info for METADATA
1267 entry, with which a system updater can be fetched the package metadata prior
1268 to downloading the entire package.
1269 """
1270
1271 def __init__(self):
1272 super(NonAbOtaPropertyFiles, self).__init__()
1273 self.name = 'ota-property-files'
1274
1275
Tao Baod3fc38a2018-03-08 16:09:01 -08001276def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001277 """Finalizes the metadata and signs an A/B OTA package.
1278
1279 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1280 that contains the offsets and sizes for the ZIP entries. An example
1281 property-files string is as follows.
1282
1283 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1284
1285 OTA server can pass down this string, in addition to the package URL, to the
1286 system update client. System update client can then fetch individual ZIP
1287 entries (ZIP_STORED) directly at the given offset of the URL.
1288
1289 Args:
1290 metadata: The metadata dict for the package.
1291 input_file: The input ZIP filename that doesn't contain the package METADATA
1292 entry yet.
1293 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001294 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001295 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001296
Tao Baod2ce2ed2018-03-16 12:59:42 -07001297 def ComputeAllPropertyFiles(input_file, needed_property_files):
1298 # Write the current metadata entry with placeholders.
1299 with zipfile.ZipFile(input_file) as input_zip:
1300 for property_files in needed_property_files:
1301 metadata[property_files.name] = property_files.Compute(input_zip)
1302 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001303
Tao Baod2ce2ed2018-03-16 12:59:42 -07001304 if METADATA_NAME in namelist:
1305 common.ZipDelete(input_file, METADATA_NAME)
1306 output_zip = zipfile.ZipFile(input_file, 'a')
1307 WriteMetadata(metadata, output_zip)
1308 common.ZipClose(output_zip)
1309
1310 if OPTIONS.no_signing:
1311 return input_file
1312
Tao Bao491d7e22018-02-21 13:17:22 -08001313 prelim_signing = common.MakeTempFile(suffix='.zip')
1314 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001315 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001316
Tao Baod2ce2ed2018-03-16 12:59:42 -07001317 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1318 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1319 for property_files in needed_property_files:
1320 metadata[property_files.name] = property_files.Finalize(
1321 prelim_signing_zip, len(metadata[property_files.name]))
1322
1323 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1324 # entries, as well as padding the entry headers. We do a preliminary signing
1325 # (with an incomplete metadata entry) to allow that to happen. Then compute
1326 # the ZIP entry offsets, write back the final metadata and do the final
1327 # signing.
1328 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1329 try:
1330 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1331 except PropertyFiles.InsufficientSpaceException:
1332 # Even with the preliminary signing, the entry orders may change
1333 # dramatically, which leads to insufficiently reserved space during the
1334 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1335 # preliminary signing works, based on the already ordered ZIP entries, to
1336 # address the issue.
1337 prelim_signing = ComputeAllPropertyFiles(
1338 prelim_signing, needed_property_files)
1339 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001340
1341 # Replace the METADATA entry.
1342 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001343 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001344 WriteMetadata(metadata, output_zip)
1345 common.ZipClose(output_zip)
1346
1347 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001348 if OPTIONS.no_signing:
1349 output_file = prelim_signing
1350 else:
1351 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001352
1353 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001354 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001355 for property_files in needed_property_files:
1356 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001357
1358
Tao Bao491d7e22018-02-21 13:17:22 -08001359def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001360 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1361 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001362
Tao Bao481bab82017-12-21 11:23:09 -08001363 target_api_version = target_info["recovery_api_version"]
1364 source_api_version = source_info["recovery_api_version"]
1365 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001366 print("WARNING: generating edify script for a source that "
1367 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001368
Tao Bao481bab82017-12-21 11:23:09 -08001369 script = edify_generator.EdifyGenerator(
1370 source_api_version, target_info, fstab=source_info["fstab"])
1371
1372 if target_info.oem_props or source_info.oem_props:
1373 if not OPTIONS.oem_no_mount:
1374 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001375
Tao Baodf3a48b2018-01-10 16:30:43 -08001376 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001377
Tao Bao491d7e22018-02-21 13:17:22 -08001378 if not OPTIONS.no_signing:
1379 staging_file = common.MakeTempFile(suffix='.zip')
1380 else:
1381 staging_file = output_file
1382
1383 output_zip = zipfile.ZipFile(
1384 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1385
Geremy Condra36bd3652014-02-06 19:45:10 -08001386 device_specific = common.DeviceSpecificParams(
1387 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001388 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001389 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001390 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001391 output_zip=output_zip,
1392 script=script,
1393 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001394 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001395
Geremy Condra36bd3652014-02-06 19:45:10 -08001396 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001397 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001398 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001399 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001400 updating_boot = (not OPTIONS.two_step and
1401 (source_boot.data != target_boot.data))
1402
Geremy Condra36bd3652014-02-06 19:45:10 -08001403 target_recovery = common.GetBootableImage(
1404 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001405
Tao Baoe709b092018-02-07 12:40:00 -08001406 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1407 # shared blocks (i.e. some blocks will show up in multiple files' block
1408 # list). We can only allocate such shared blocks to the first "owner", and
1409 # disable imgdiff for all later occurrences.
1410 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1411 target_info.get('ext4_share_dup_blocks') == "true")
1412 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1413 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001414
1415 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1416 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001417 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001418 allow_shared_blocks,
1419 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001420
Tao Bao0582cb62017-12-21 11:47:01 -08001421 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001422 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001423 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001424
Tao Baof8acad12016-07-07 09:09:58 -07001425 # Check the first block of the source system partition for remount R/W only
1426 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001427 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001428 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001429 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1430 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1431 # b) the blocks listed in block map may not contain all the bytes for a given
1432 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001433 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001434 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1435 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001436 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001437 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001438 version=blockimgdiff_version,
1439 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001440
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001441 if HasVendorPartition(target_zip):
1442 if not HasVendorPartition(source_zip):
1443 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001444 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1445 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001446 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1447 "vendor", 4096, target_info)
1448 vendor_tgt = common.GetSparseImage(
1449 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1450 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001451
1452 # Check first block of vendor partition for remount R/W only if
1453 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001454 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001455 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001456 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001457 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001458 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001459 version=blockimgdiff_version,
1460 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001461 else:
1462 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001463
Tao Baobcd1d162017-08-26 13:10:26 -07001464 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001465 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001466
Tao Bao481bab82017-12-21 11:23:09 -08001467 # Assertions (e.g. device properties check).
1468 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001469 device_specific.IncrementalOTA_Assertions()
1470
1471 # Two-step incremental package strategy (in chronological order,
1472 # which is *not* the order in which the generated script has
1473 # things):
1474 #
1475 # if stage is not "2/3" or "3/3":
1476 # do verification on current system
1477 # write recovery image to boot partition
1478 # set stage to "2/3"
1479 # reboot to boot partition and restart recovery
1480 # else if stage is "2/3":
1481 # write recovery image to recovery partition
1482 # set stage to "3/3"
1483 # reboot to recovery partition and restart recovery
1484 # else:
1485 # (stage must be "3/3")
1486 # perform update:
1487 # patch system files, etc.
1488 # force full install of new boot image
1489 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001490 # complete script normally
1491 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001492
1493 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001494 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001495 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001496 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001497 assert fs.fs_type.upper() == "EMMC", \
1498 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001499 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001500 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1501 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001502if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001503""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001504
1505 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1506 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001507 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001508 script.WriteRawImage("/recovery", "recovery.img")
1509 script.AppendExtra("""
1510set_stage("%(bcb_dev)s", "3/3");
1511reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001512else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001513""" % bcb_dev)
1514
Tao Baod42e97e2016-11-30 12:11:57 -08001515 # Stage 1/3: (a) Verify the current system.
1516 script.Comment("Stage 1/3")
1517
Tao Bao6c55a8a2015-04-08 15:30:27 -07001518 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001519 script.Print("Source: {}".format(source_info.fingerprint))
1520 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001521
Geremy Condra36bd3652014-02-06 19:45:10 -08001522 script.Print("Verifying current system...")
1523
1524 device_specific.IncrementalOTA_VerifyBegin()
1525
Tao Bao481bab82017-12-21 11:23:09 -08001526 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001527
Tao Baod8d14be2016-02-04 14:26:02 -08001528 # Check the required cache size (i.e. stashed blocks).
1529 size = []
1530 if system_diff:
1531 size.append(system_diff.required_cache)
1532 if vendor_diff:
1533 size.append(vendor_diff.required_cache)
1534
Geremy Condra36bd3652014-02-06 19:45:10 -08001535 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001536 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001537 d = common.Difference(target_boot, source_boot)
1538 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001539 if d is None:
1540 include_full_boot = True
1541 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1542 else:
1543 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001544
Tao Bao89fbb0f2017-01-10 10:47:58 -08001545 print("boot target: %d source: %d diff: %d" % (
1546 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001547
Tao Bao51216552018-08-26 11:53:15 -07001548 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001549
Tao Bao51216552018-08-26 11:53:15 -07001550 script.PatchPartitionCheck(
1551 "{}:{}:{}:{}".format(
1552 boot_type, boot_device, target_boot.size, target_boot.sha1),
1553 "{}:{}:{}:{}".format(
1554 boot_type, boot_device, source_boot.size, source_boot.sha1))
1555
Tao Baod8d14be2016-02-04 14:26:02 -08001556 size.append(target_boot.size)
1557
1558 if size:
1559 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001560
1561 device_specific.IncrementalOTA_VerifyEnd()
1562
1563 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001564 # Stage 1/3: (b) Write recovery image to /boot.
1565 _WriteRecoveryImageToBoot(script, output_zip)
1566
Geremy Condra36bd3652014-02-06 19:45:10 -08001567 script.AppendExtra("""
1568set_stage("%(bcb_dev)s", "2/3");
1569reboot_now("%(bcb_dev)s", "");
1570else
1571""" % bcb_dev)
1572
Tao Baod42e97e2016-11-30 12:11:57 -08001573 # Stage 3/3: Make changes.
1574 script.Comment("Stage 3/3")
1575
Jesse Zhao75bcea02015-01-06 10:59:53 -08001576 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001577 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001578 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001579 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001580
Geremy Condra36bd3652014-02-06 19:45:10 -08001581 script.Comment("---- start making changes here ----")
1582
1583 device_specific.IncrementalOTA_InstallBegin()
1584
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001585 system_diff.WriteScript(script, output_zip,
Tao Bao76def242017-11-21 09:25:31 -08001586 progress=0.8 if vendor_diff else 0.9,
1587 write_verify_script=OPTIONS.verify)
Tao Bao68658c02015-06-01 13:40:49 -07001588
Doug Zongkerfc44a512014-08-26 13:10:25 -07001589 if vendor_diff:
Tao Bao76def242017-11-21 09:25:31 -08001590 vendor_diff.WriteScript(script, output_zip, progress=0.1,
1591 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001592
1593 if OPTIONS.two_step:
1594 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1595 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001596 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001597
1598 if not OPTIONS.two_step:
1599 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001600 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001601 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001602 script.Print("Installing boot image...")
1603 script.WriteRawImage("/boot", "boot.img")
1604 else:
1605 # Produce the boot image by applying a patch to the current
1606 # contents of the boot partition, and write it back to the
1607 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001608 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001609 script.Print("Patching boot image...")
1610 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001611 script.PatchPartition(
1612 '{}:{}:{}:{}'.format(
1613 boot_type, boot_device, target_boot.size, target_boot.sha1),
1614 '{}:{}:{}:{}'.format(
1615 boot_type, boot_device, source_boot.size, source_boot.sha1),
1616 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001617 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001618 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001619
1620 # Do device-specific installation (eg, write radio image).
1621 device_specific.IncrementalOTA_InstallEnd()
1622
1623 if OPTIONS.extra_script is not None:
1624 script.AppendExtra(OPTIONS.extra_script)
1625
Doug Zongker922206e2014-03-04 13:16:24 -08001626 if OPTIONS.wipe_user_data:
1627 script.Print("Erasing user data...")
1628 script.FormatPartition("/data")
1629
Geremy Condra36bd3652014-02-06 19:45:10 -08001630 if OPTIONS.two_step:
1631 script.AppendExtra("""
1632set_stage("%(bcb_dev)s", "");
1633endif;
1634endif;
1635""" % bcb_dev)
1636
1637 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001638 # For downgrade OTAs, we prefer to use the update-binary in the source
1639 # build that is actually newer than the one in the target build.
1640 if OPTIONS.downgrade:
1641 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1642 else:
1643 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001644 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001645
1646 # We haven't written the metadata entry yet, which will be handled in
1647 # FinalizeMetadata().
1648 common.ZipClose(output_zip)
1649
1650 # Sign the generated zip package unless no_signing is specified.
1651 needed_property_files = (
1652 NonAbOtaPropertyFiles(),
1653 )
1654 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001655
Doug Zongker32b527d2014-03-04 10:03:02 -08001656
Tao Bao15a146a2018-02-21 16:06:59 -08001657def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001658 """Returns a target-files.zip file for generating secondary payload.
1659
1660 Although the original target-files.zip already contains secondary slot
1661 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1662 ones without _other suffix. Note that we cannot instead modify the names in
1663 META/ab_partitions.txt, because there are no matching partitions on device.
1664
1665 For the partitions that don't have secondary images, the ones for primary
1666 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1667 bootloader images in the inactive slot.
1668
1669 Args:
1670 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001671 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001672
1673 Returns:
1674 The filename of the target-files.zip for generating secondary payload.
1675 """
1676 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1677 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1678
Tao Baodba59ee2018-01-09 13:21:02 -08001679 with zipfile.ZipFile(input_file, 'r') as input_zip:
1680 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001681 namelist = input_zip.namelist()
1682
1683 # Additionally unzip 'RADIO/*' if exists.
1684 unzip_pattern = UNZIP_PATTERN[:]
1685 if any([entry.startswith('RADIO/') for entry in namelist]):
1686 unzip_pattern.append('RADIO/*')
1687 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001688
1689 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001690 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1691 if info.filename == 'IMAGES/system_other.img':
1692 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1693
1694 # Primary images and friends need to be skipped explicitly.
1695 elif info.filename in ('IMAGES/system.img',
1696 'IMAGES/system.map'):
1697 pass
1698
Tao Bao15a146a2018-02-21 16:06:59 -08001699 # Skip copying the postinstall config if requested.
1700 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1701 pass
1702
Tao Bao12489802018-07-12 14:47:38 -07001703 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001704 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1705
Tao Baof7140c02018-01-30 17:09:24 -08001706 common.ZipClose(target_zip)
1707
1708 return target_file
1709
1710
Tao Bao15a146a2018-02-21 16:06:59 -08001711def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1712 """Returns a target-files.zip that's not containing postinstall_config.txt.
1713
1714 This allows brillo_update_payload script to skip writing all the postinstall
1715 hooks in the generated payload. The input target-files.zip file will be
1716 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1717 contain the postinstall_config.txt entry, the input file will be returned.
1718
1719 Args:
1720 input_file: The input target-files.zip filename.
1721
1722 Returns:
1723 The filename of target-files.zip that doesn't contain postinstall config.
1724 """
1725 # We should only make a copy if postinstall_config entry exists.
1726 with zipfile.ZipFile(input_file, 'r') as input_zip:
1727 if POSTINSTALL_CONFIG not in input_zip.namelist():
1728 return input_file
1729
1730 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1731 shutil.copyfile(input_file, target_file)
1732 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1733 return target_file
1734
1735
Tao Baoc098e9e2016-01-07 13:03:56 -08001736def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1737 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001738 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001739 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001740 if not OPTIONS.no_signing:
1741 staging_file = common.MakeTempFile(suffix='.zip')
1742 else:
1743 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001744 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001745 compression=zipfile.ZIP_DEFLATED)
1746
Tao Bao481bab82017-12-21 11:23:09 -08001747 if source_file is not None:
1748 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1749 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1750 else:
1751 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1752 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001753
Tao Bao481bab82017-12-21 11:23:09 -08001754 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001755 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001756
Tao Bao15a146a2018-02-21 16:06:59 -08001757 if OPTIONS.skip_postinstall:
1758 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1759
Tao Bao40b18822018-01-30 18:19:04 -08001760 # Generate payload.
1761 payload = Payload()
1762
1763 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001764 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001765 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001766 else:
1767 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001768 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001769
Tao Bao40b18822018-01-30 18:19:04 -08001770 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001771
Tao Bao40b18822018-01-30 18:19:04 -08001772 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001773 payload_signer = PayloadSigner()
1774 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001775
Tao Bao40b18822018-01-30 18:19:04 -08001776 # Write the payload into output zip.
1777 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001778
Tao Baof7140c02018-01-30 17:09:24 -08001779 # Generate and include the secondary payload that installs secondary images
1780 # (e.g. system_other.img).
1781 if OPTIONS.include_secondary:
1782 # We always include a full payload for the secondary slot, even when
1783 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001784 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1785 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001786 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001787 secondary_payload.Generate(secondary_target_file,
1788 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001789 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001790 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001791
Tianjie Xucfa86222016-03-07 16:31:19 -08001792 # If dm-verity is supported for the device, copy contents of care_map
1793 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001794 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001795 if (target_info.get("verity") == "true" or
1796 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001797 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1798 "META/" + x in target_zip.namelist()]
1799
1800 # Adds care_map if either the protobuf format or the plain text one exists.
1801 if care_map_list:
1802 care_map_name = care_map_list[0]
1803 care_map_data = target_zip.read("META/" + care_map_name)
1804 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001805 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001806 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001807 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001808 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001809 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001810
Tao Baobcd1d162017-08-26 13:10:26 -07001811 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001812 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001813
Tao Bao21803d32017-04-19 10:16:09 -07001814 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001815
Tao Baofe5b69a2018-03-02 09:47:43 -08001816 # We haven't written the metadata entry yet, which will be handled in
1817 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001818 common.ZipClose(output_zip)
1819
Tao Bao85f16982018-03-08 16:28:33 -08001820 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1821 # all the info of the latter. However, system updaters and OTA servers need to
1822 # take time to switch to the new flag. We keep both of the flags for
1823 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001824 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001825 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001826 StreamingPropertyFiles(),
1827 )
1828 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001829
Tao Baoc098e9e2016-01-07 13:03:56 -08001830
Doug Zongkereef39442009-04-02 12:14:19 -07001831def main(argv):
1832
1833 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001834 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001835 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001836 elif o in ("-i", "--incremental_from"):
1837 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001838 elif o == "--full_radio":
1839 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001840 elif o == "--full_bootloader":
1841 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001842 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001843 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001844 elif o == "--downgrade":
1845 OPTIONS.downgrade = True
1846 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001847 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001848 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001849 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001850 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001851 elif o == "--oem_no_mount":
1852 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001853 elif o in ("-e", "--extra_script"):
1854 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001855 elif o in ("-t", "--worker_threads"):
1856 if a.isdigit():
1857 OPTIONS.worker_threads = int(a)
1858 else:
1859 raise ValueError("Cannot parse value %r for option %r - only "
1860 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001861 elif o in ("-2", "--two_step"):
1862 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001863 elif o == "--include_secondary":
1864 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001865 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001866 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001867 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001868 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001869 elif o == "--block":
1870 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001871 elif o in ("-b", "--binary"):
1872 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001873 elif o == "--stash_threshold":
1874 try:
1875 OPTIONS.stash_threshold = float(a)
1876 except ValueError:
1877 raise ValueError("Cannot parse value %r for option %r - expecting "
1878 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001879 elif o == "--log_diff":
1880 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001881 elif o == "--payload_signer":
1882 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001883 elif o == "--payload_signer_args":
1884 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001885 elif o == "--extracted_input_target_files":
1886 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001887 elif o == "--skip_postinstall":
1888 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001889 else:
1890 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001891 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001892
1893 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001894 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001895 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001896 "package_key=",
1897 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001898 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001899 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001900 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001901 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001902 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001903 "extra_script=",
1904 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001905 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001906 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001907 "no_signing",
1908 "block",
1909 "binary=",
1910 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001911 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001912 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001913 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001914 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001915 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001916 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001917 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001918 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001919 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001920
1921 if len(args) != 2:
1922 common.Usage(__doc__)
1923 sys.exit(1)
1924
Tao Bao5d182562016-02-23 11:38:39 -08001925 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08001926 # We should only allow downgrading incrementals (as opposed to full).
1927 # Otherwise the device may go back from arbitrary build with this full
1928 # OTA package.
1929 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001930 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001931
Tao Bao2db13852018-01-08 22:28:57 -08001932 # Load the build info dicts from the zip directly or the extracted input
1933 # directory. We don't need to unzip the entire target-files zips, because they
1934 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1935 # When loading the info dicts, we don't need to provide the second parameter
1936 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1937 # some properties with their actual paths, such as 'selinux_fc',
1938 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001939 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001940 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001941 else:
Tao Bao2db13852018-01-08 22:28:57 -08001942 with zipfile.ZipFile(args[0], 'r') as input_zip:
1943 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001944
Tao Bao2db13852018-01-08 22:28:57 -08001945 if OPTIONS.verbose:
1946 print("--- target info ---")
1947 common.DumpInfoDict(OPTIONS.info_dict)
1948
1949 # Load the source build dict if applicable.
1950 if OPTIONS.incremental_source is not None:
1951 OPTIONS.target_info_dict = OPTIONS.info_dict
1952 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1953 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1954
1955 if OPTIONS.verbose:
1956 print("--- source info ---")
1957 common.DumpInfoDict(OPTIONS.source_info_dict)
1958
1959 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001960 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1961
Tao Baoc098e9e2016-01-07 13:03:56 -08001962 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1963
Christian Oderf63e2cd2017-05-01 22:30:15 +02001964 # Use the default key to sign the package if not specified with package_key.
1965 # package_keys are needed on ab_updates, so always define them if an
1966 # ab_update is getting created.
1967 if not OPTIONS.no_signing or ab_update:
1968 if OPTIONS.package_key is None:
1969 OPTIONS.package_key = OPTIONS.info_dict.get(
1970 "default_system_dev_certificate",
1971 "build/target/product/security/testkey")
1972 # Get signing keys
1973 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1974
Tao Baoc098e9e2016-01-07 13:03:56 -08001975 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001976 WriteABOTAPackageWithBrilloScript(
1977 target_file=args[0],
1978 output_file=args[1],
1979 source_file=OPTIONS.incremental_source)
1980
Tao Bao89fbb0f2017-01-10 10:47:58 -08001981 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001982 return
1983
Tao Bao2db13852018-01-08 22:28:57 -08001984 # Sanity check the loaded info dicts first.
1985 if OPTIONS.info_dict.get("no_recovery") == "true":
1986 raise common.ExternalError(
1987 "--- target build has specified no recovery ---")
1988
1989 # Non-A/B OTAs rely on /cache partition to store temporary files.
1990 cache_size = OPTIONS.info_dict.get("cache_size")
1991 if cache_size is None:
1992 print("--- can't determine the cache partition size ---")
1993 OPTIONS.cache_size = cache_size
1994
Doug Zongker1c390a22009-05-14 19:06:36 -07001995 if OPTIONS.extra_script is not None:
1996 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1997
Dan Willemsencea5cd22017-03-21 14:44:27 -07001998 if OPTIONS.extracted_input is not None:
1999 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002000 else:
2001 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002002 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002003 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002004
Tao Bao2db13852018-01-08 22:28:57 -08002005 # If the caller explicitly specified the device-specific extensions path via
2006 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2007 # is present in the target target_files. Otherwise, take the path of the file
2008 # from 'tool_extensions' in the info dict and look for that in the local
2009 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002010 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002011 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2012 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002013 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002014 OPTIONS.device_specific = from_input
2015 else:
Tao Bao2db13852018-01-08 22:28:57 -08002016 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002017
Doug Zongker37974732010-09-16 17:44:38 -07002018 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002019 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002020
Tao Bao767e3ac2015-11-10 12:19:19 -08002021 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002022 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002023 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002024 WriteFullOTAPackage(
2025 input_zip,
2026 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002027
Tao Bao32b80dc2018-01-08 22:50:47 -08002028 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002029 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002030 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002031 OPTIONS.source_tmp = common.UnzipTemp(
2032 OPTIONS.incremental_source, UNZIP_PATTERN)
2033 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2034 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002035 WriteBlockIncrementalOTAPackage(
2036 input_zip,
2037 source_zip,
2038 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002039
2040 if OPTIONS.log_diff:
2041 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002042 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002043 target_files_diff.recursiveDiff(
2044 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002045
Tao Bao89fbb0f2017-01-10 10:47:58 -08002046 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002047
2048
2049if __name__ == '__main__':
2050 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002051 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002052 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002053 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002054 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002055 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002056 finally:
2057 common.Cleanup()