blob: 755eda9ebefba453115ce4639e6aa4099bcd1fa8 [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
397 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
398 stderr=subprocess.STDOUT)
399 stdoutdata, _ = get_signing_key.communicate()
400 assert get_signing_key.returncode == 0, \
401 "Failed to get signing key: {}".format(stdoutdata)
402
403 self.signer = "openssl"
404 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
405 "-pkeyopt", "digest:sha256"]
406 else:
407 self.signer = OPTIONS.payload_signer
408 self.signer_args = OPTIONS.payload_signer_args
409
410 def Sign(self, in_file):
411 """Signs the given input file. Returns the output filename."""
412 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
413 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
414 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
415 stdoutdata, _ = signing.communicate()
416 assert signing.returncode == 0, \
417 "Failed to sign the input file: {}".format(stdoutdata)
418 return out_file
419
420
Tao Bao40b18822018-01-30 18:19:04 -0800421class Payload(object):
422 """Manages the creation and the signing of an A/B OTA Payload."""
423
424 PAYLOAD_BIN = 'payload.bin'
425 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800426 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
427 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800428
Tao Bao667ff572018-02-10 00:02:40 -0800429 def __init__(self, secondary=False):
430 """Initializes a Payload instance.
431
432 Args:
433 secondary: Whether it's generating a secondary payload (default: False).
434 """
Tao Bao40b18822018-01-30 18:19:04 -0800435 # The place where the output from the subprocess should go.
436 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
437 self.payload_file = None
438 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800439 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800440
441 def Generate(self, target_file, source_file=None, additional_args=None):
442 """Generates a payload from the given target-files zip(s).
443
444 Args:
445 target_file: The filename of the target build target-files zip.
446 source_file: The filename of the source build target-files zip; or None if
447 generating a full OTA.
448 additional_args: A list of additional args that should be passed to
449 brillo_update_payload script; or None.
450 """
451 if additional_args is None:
452 additional_args = []
453
454 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
455 cmd = ["brillo_update_payload", "generate",
456 "--payload", payload_file,
457 "--target_image", target_file]
458 if source_file is not None:
459 cmd.extend(["--source_image", source_file])
460 cmd.extend(additional_args)
461 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
462 stdoutdata, _ = p.communicate()
463 assert p.returncode == 0, \
464 "brillo_update_payload generate failed: {}".format(stdoutdata)
465
466 self.payload_file = payload_file
467 self.payload_properties = None
468
469 def Sign(self, payload_signer):
470 """Generates and signs the hashes of the payload and metadata.
471
472 Args:
473 payload_signer: A PayloadSigner() instance that serves the signing work.
474
475 Raises:
476 AssertionError: On any failure when calling brillo_update_payload script.
477 """
478 assert isinstance(payload_signer, PayloadSigner)
479
480 # 1. Generate hashes of the payload and metadata files.
481 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
482 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
483 cmd = ["brillo_update_payload", "hash",
484 "--unsigned_payload", self.payload_file,
485 "--signature_size", "256",
486 "--metadata_hash_file", metadata_sig_file,
487 "--payload_hash_file", payload_sig_file]
488 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
489 p1.communicate()
490 assert p1.returncode == 0, "brillo_update_payload hash failed"
491
492 # 2. Sign the hashes.
493 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
494 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
495
496 # 3. Insert the signatures back into the payload file.
497 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
498 suffix=".bin")
499 cmd = ["brillo_update_payload", "sign",
500 "--unsigned_payload", self.payload_file,
501 "--payload", signed_payload_file,
502 "--signature_size", "256",
503 "--metadata_signature_file", signed_metadata_sig_file,
504 "--payload_signature_file", signed_payload_sig_file]
505 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
506 p1.communicate()
507 assert p1.returncode == 0, "brillo_update_payload sign failed"
508
509 # 4. Dump the signed payload properties.
510 properties_file = common.MakeTempFile(prefix="payload-properties-",
511 suffix=".txt")
512 cmd = ["brillo_update_payload", "properties",
513 "--payload", signed_payload_file,
514 "--properties_file", properties_file]
515 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
516 p1.communicate()
517 assert p1.returncode == 0, "brillo_update_payload properties failed"
518
Tao Bao667ff572018-02-10 00:02:40 -0800519 if self.secondary:
520 with open(properties_file, "a") as f:
521 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
522
Tao Bao40b18822018-01-30 18:19:04 -0800523 if OPTIONS.wipe_user_data:
524 with open(properties_file, "a") as f:
525 f.write("POWERWASH=1\n")
526
527 self.payload_file = signed_payload_file
528 self.payload_properties = properties_file
529
Tao Bao667ff572018-02-10 00:02:40 -0800530 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800531 """Writes the payload to the given zip.
532
533 Args:
534 output_zip: The output ZipFile instance.
535 """
536 assert self.payload_file is not None
537 assert self.payload_properties is not None
538
Tao Bao667ff572018-02-10 00:02:40 -0800539 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800540 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
541 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
542 else:
543 payload_arcname = Payload.PAYLOAD_BIN
544 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
545
Tao Bao40b18822018-01-30 18:19:04 -0800546 # Add the signed payload file and properties into the zip. In order to
547 # support streaming, we pack them as ZIP_STORED. So these entries can be
548 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800549 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800550 compress_type=zipfile.ZIP_STORED)
551 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800552 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800553 compress_type=zipfile.ZIP_STORED)
554
555
Doug Zongkereef39442009-04-02 12:14:19 -0700556def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200557 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700558
Doug Zongker951495f2009-08-14 12:44:19 -0700559 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
560 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700561
562
Tao Bao481bab82017-12-21 11:23:09 -0800563def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800564 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800565 if not oem_source:
566 return None
567
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800568 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800569 for oem_file in oem_source:
570 with open(oem_file) as fp:
571 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800572 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700573
Doug Zongkereef39442009-04-02 12:14:19 -0700574
Tao Baod42e97e2016-11-30 12:11:57 -0800575def _WriteRecoveryImageToBoot(script, output_zip):
576 """Find and write recovery image to /boot in two-step OTA.
577
578 In two-step OTAs, we write recovery image to /boot as the first step so that
579 we can reboot to there and install a new recovery image to /recovery.
580 A special "recovery-two-step.img" will be preferred, which encodes the correct
581 path of "/boot". Otherwise the device may show "device is corrupt" message
582 when booting into /boot.
583
584 Fall back to using the regular recovery.img if the two-step recovery image
585 doesn't exist. Note that rebuilding the special image at this point may be
586 infeasible, because we don't have the desired boot signer and keys when
587 calling ota_from_target_files.py.
588 """
589
590 recovery_two_step_img_name = "recovery-two-step.img"
591 recovery_two_step_img_path = os.path.join(
592 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
593 if os.path.exists(recovery_two_step_img_path):
594 recovery_two_step_img = common.GetBootableImage(
595 recovery_two_step_img_name, recovery_two_step_img_name,
596 OPTIONS.input_tmp, "RECOVERY")
597 common.ZipWriteStr(
598 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800599 print("two-step package: using %s in stage 1/3" % (
600 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800601 script.WriteRawImage("/boot", recovery_two_step_img_name)
602 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800603 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800604 # The "recovery.img" entry has been written into package earlier.
605 script.WriteRawImage("/boot", "recovery.img")
606
607
Doug Zongkerc9253822014-02-04 12:17:58 -0800608def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700609 namelist = [name for name in target_files_zip.namelist()]
610 return ("SYSTEM/recovery-from-boot.p" in namelist or
611 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700612
Tao Bao457cbf62017-03-06 09:56:01 -0800613
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700614def HasVendorPartition(target_files_zip):
615 try:
616 target_files_zip.getinfo("VENDOR/")
617 return True
618 except KeyError:
619 return False
620
Tao Bao457cbf62017-03-06 09:56:01 -0800621
Tao Bao481bab82017-12-21 11:23:09 -0800622def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700623 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800624 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700625
626
Tao Bao481bab82017-12-21 11:23:09 -0800627def WriteFingerprintAssertion(script, target_info, source_info):
628 source_oem_props = source_info.oem_props
629 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700630
Tao Bao481bab82017-12-21 11:23:09 -0800631 if source_oem_props is None and target_oem_props is None:
632 script.AssertSomeFingerprint(
633 source_info.fingerprint, target_info.fingerprint)
634 elif source_oem_props is not None and target_oem_props is not None:
635 script.AssertSomeThumbprint(
636 target_info.GetBuildProp("ro.build.thumbprint"),
637 source_info.GetBuildProp("ro.build.thumbprint"))
638 elif source_oem_props is None and target_oem_props is not None:
639 script.AssertFingerprintOrThumbprint(
640 source_info.fingerprint,
641 target_info.GetBuildProp("ro.build.thumbprint"))
642 else:
643 script.AssertFingerprintOrThumbprint(
644 target_info.fingerprint,
645 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700646
Doug Zongkerfc44a512014-08-26 13:10:25 -0700647
Tao Bao481bab82017-12-21 11:23:09 -0800648def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
649 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700650 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700651
652 Metadata used for on-device compatibility verification is retrieved from
653 target_zip then added to compatibility.zip which is added to the output_zip
654 archive.
655
Tao Baobcd1d162017-08-26 13:10:26 -0700656 Compatibility archive should only be included for devices that have enabled
657 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700658
659 Args:
660 target_zip: Zip file containing the source files to be included for OTA.
661 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800662 target_info: The BuildInfo instance that holds the target build info.
663 source_info: The BuildInfo instance that holds the source build info, if
664 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700665 """
666
Tao Baobcd1d162017-08-26 13:10:26 -0700667 def AddCompatibilityArchive(system_updated, vendor_updated):
668 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700669
Tao Baobcd1d162017-08-26 13:10:26 -0700670 Args:
671 system_updated: If True, the system image will be updated and therefore
672 its metadata should be included.
673 vendor_updated: If True, the vendor image will be updated and therefore
674 its metadata should be included.
675 """
676 # Determine what metadata we need. Files are names relative to META/.
677 compatibility_files = []
678 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
679 system_metadata = ("system_manifest.xml", "system_matrix.xml")
680 if vendor_updated:
681 compatibility_files += vendor_metadata
682 if system_updated:
683 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700684
Tao Baobcd1d162017-08-26 13:10:26 -0700685 # Create new archive.
686 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800687 compatibility_archive_zip = zipfile.ZipFile(
688 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700689
Tao Baobcd1d162017-08-26 13:10:26 -0700690 # Add metadata.
691 for file_name in compatibility_files:
692 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700693
Tao Baobcd1d162017-08-26 13:10:26 -0700694 if target_file_name in target_zip.namelist():
695 data = target_zip.read(target_file_name)
696 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700697
Tao Baobcd1d162017-08-26 13:10:26 -0700698 # Ensure files are written before we copy into output_zip.
699 compatibility_archive_zip.close()
700
701 # Only add the archive if we have any compatibility info.
702 if compatibility_archive_zip.namelist():
703 common.ZipWrite(output_zip, compatibility_archive.name,
704 arcname="compatibility.zip",
705 compress_type=zipfile.ZIP_STORED)
706
707 # Will only proceed if the target has enabled the Treble support (as well as
708 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800709 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700710 return
711
Tao Baobcd1d162017-08-26 13:10:26 -0700712 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800713 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700714 AddCompatibilityArchive(True, True)
715 return
716
Tao Bao481bab82017-12-21 11:23:09 -0800717 source_fp = source_info.fingerprint
718 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700719 system_updated = source_fp != target_fp
720
Tao Baoea6cbd02018-09-05 13:06:37 -0700721 source_fp_vendor = source_info.vendor_fingerprint
722 target_fp_vendor = target_info.vendor_fingerprint
723 # vendor build fingerprints could be possibly blacklisted at build time. For
724 # such a case, we consider the vendor images being changed.
725 if source_fp_vendor is None or target_fp_vendor is None:
726 vendor_updated = True
727 else:
728 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700729
730 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700731
732
Tao Bao491d7e22018-02-21 13:17:22 -0800733def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800734 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700735
Tao Bao481bab82017-12-21 11:23:09 -0800736 # We don't know what version it will be installed on top of. We expect the API
737 # just won't change very often. Similarly for fstab, it might have changed in
738 # the target build.
739 target_api_version = target_info["recovery_api_version"]
740 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700741
Tao Bao481bab82017-12-21 11:23:09 -0800742 if target_info.oem_props and not OPTIONS.oem_no_mount:
743 target_info.WriteMountOemScript(script)
744
Tao Baodf3a48b2018-01-10 16:30:43 -0800745 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700746
Tao Bao491d7e22018-02-21 13:17:22 -0800747 if not OPTIONS.no_signing:
748 staging_file = common.MakeTempFile(suffix='.zip')
749 else:
750 staging_file = output_file
751
752 output_zip = zipfile.ZipFile(
753 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
754
Doug Zongker05d3dea2009-06-22 11:32:31 -0700755 device_specific = common.DeviceSpecificParams(
756 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800757 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700758 output_zip=output_zip,
759 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700760 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700761 metadata=metadata,
762 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700763
Tao Bao457cbf62017-03-06 09:56:01 -0800764 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800765
Tao Bao481bab82017-12-21 11:23:09 -0800766 # Assertions (e.g. downgrade check, device properties check).
767 ts = target_info.GetBuildProp("ro.build.date.utc")
768 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700769 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700770
Tao Bao481bab82017-12-21 11:23:09 -0800771 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700772 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800773
774 # Two-step package strategy (in chronological order, which is *not*
775 # the order in which the generated script has things):
776 #
777 # if stage is not "2/3" or "3/3":
778 # write recovery image to boot partition
779 # set stage to "2/3"
780 # reboot to boot partition and restart recovery
781 # else if stage is "2/3":
782 # write recovery image to recovery partition
783 # set stage to "3/3"
784 # reboot to recovery partition and restart recovery
785 # else:
786 # (stage must be "3/3")
787 # set stage to ""
788 # do normal full package installation:
789 # wipe and install system, boot image, etc.
790 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700791 # complete script normally
792 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800793
794 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
795 OPTIONS.input_tmp, "RECOVERY")
796 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800797 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800798 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800799 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800800 assert fs.fs_type.upper() == "EMMC", \
801 "two-step packages only supported on devices with EMMC /misc partitions"
802 bcb_dev = {"bcb_dev": fs.device}
803 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
804 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700805if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800806""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800807
808 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
809 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800810 script.WriteRawImage("/recovery", "recovery.img")
811 script.AppendExtra("""
812set_stage("%(bcb_dev)s", "3/3");
813reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700814else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800815""" % bcb_dev)
816
Tao Baod42e97e2016-11-30 12:11:57 -0800817 # Stage 3/3: Make changes.
818 script.Comment("Stage 3/3")
819
Tao Bao6c55a8a2015-04-08 15:30:27 -0700820 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800821 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700822
Doug Zongkere5ff5902012-01-17 10:55:37 -0800823 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700824
Doug Zongker01ce19c2014-02-04 13:48:15 -0800825 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700826
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700827 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800828 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700829 if HasVendorPartition(input_zip):
830 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700831
Doug Zongker4b9596f2014-06-09 14:15:45 -0700832 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800833
Tao Baoe709b092018-02-07 12:40:00 -0800834 # See the notes in WriteBlockIncrementalOTAPackage().
835 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
836
Tao Bao457cbf62017-03-06 09:56:01 -0800837 # Full OTA is done as an "incremental" against an empty source image. This
838 # has the effect of writing new data from the package to the entire
839 # partition, but lets us reuse the updater code that writes incrementals to
840 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800841 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
842 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800843 system_tgt.ResetFileMap()
844 system_diff = common.BlockDifference("system", system_tgt, src=None)
Tao Bao76def242017-11-21 09:25:31 -0800845 system_diff.WriteScript(script, output_zip,
846 write_verify_script=OPTIONS.verify)
Doug Zongkereef39442009-04-02 12:14:19 -0700847
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700848 boot_img = common.GetBootableImage(
849 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800850
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700851 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700852 script.ShowProgress(0.1, 0)
853
Tao Baoe709b092018-02-07 12:40:00 -0800854 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
855 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800856 vendor_tgt.ResetFileMap()
857 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Tao Bao76def242017-11-21 09:25:31 -0800858 vendor_diff.WriteScript(script, output_zip,
859 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700860
Tao Bao481bab82017-12-21 11:23:09 -0800861 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700862
Tao Bao481bab82017-12-21 11:23:09 -0800863 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700864 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700865
Doug Zongker01ce19c2014-02-04 13:48:15 -0800866 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700867 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700868
Doug Zongker01ce19c2014-02-04 13:48:15 -0800869 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700870 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700871
Doug Zongker1c390a22009-05-14 19:06:36 -0700872 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700873 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700874
Doug Zongker14833602010-02-02 13:12:04 -0800875 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800876
Doug Zongker922206e2014-03-04 13:16:24 -0800877 if OPTIONS.wipe_user_data:
878 script.ShowProgress(0.1, 10)
879 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700880
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800881 if OPTIONS.two_step:
882 script.AppendExtra("""
883set_stage("%(bcb_dev)s", "");
884""" % bcb_dev)
885 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800886
887 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
888 script.Comment("Stage 1/3")
889 _WriteRecoveryImageToBoot(script, output_zip)
890
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800891 script.AppendExtra("""
892set_stage("%(bcb_dev)s", "2/3");
893reboot_now("%(bcb_dev)s", "");
894endif;
895endif;
896""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800897
Tao Bao5d182562016-02-23 11:38:39 -0800898 script.SetProgress(1)
899 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800900 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800901
902 # We haven't written the metadata entry, which will be done in
903 # FinalizeMetadata.
904 common.ZipClose(output_zip)
905
906 needed_property_files = (
907 NonAbOtaPropertyFiles(),
908 )
909 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700910
Doug Zongkerfc44a512014-08-26 13:10:25 -0700911
Doug Zongker2ea21062010-04-28 16:05:21 -0700912def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800913 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
914 common.ZipWriteStr(output_zip, METADATA_NAME, value,
915 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700916
Doug Zongkerfc44a512014-08-26 13:10:25 -0700917
Tao Bao481bab82017-12-21 11:23:09 -0800918def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800919 # Only incremental OTAs are allowed to reach here.
920 assert OPTIONS.incremental_source is not None
921
Tao Bao481bab82017-12-21 11:23:09 -0800922 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
923 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800924 is_downgrade = long(post_timestamp) < long(pre_timestamp)
925
926 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800927 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700928 raise RuntimeError(
929 "--downgrade or --override_timestamp specified but no downgrade "
930 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800931 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800932 else:
933 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700934 raise RuntimeError(
935 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
936 "Need to specify --override_timestamp OR --downgrade to allow "
937 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800938
939
Tao Baodf3a48b2018-01-10 16:30:43 -0800940def GetPackageMetadata(target_info, source_info=None):
941 """Generates and returns the metadata dict.
942
943 It generates a dict() that contains the info to be written into an OTA
944 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700945 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800946
947 Args:
948 target_info: The BuildInfo instance that holds the target build info.
949 source_info: The BuildInfo instance that holds the source build info, or
950 None if generating full OTA.
951
952 Returns:
953 A dict to be written into package metadata entry.
954 """
955 assert isinstance(target_info, BuildInfo)
956 assert source_info is None or isinstance(source_info, BuildInfo)
957
958 metadata = {
959 'post-build' : target_info.fingerprint,
960 'post-build-incremental' : target_info.GetBuildProp(
961 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800962 'post-sdk-level' : target_info.GetBuildProp(
963 'ro.build.version.sdk'),
964 'post-security-patch-level' : target_info.GetBuildProp(
965 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800966 }
967
968 if target_info.is_ab:
969 metadata['ota-type'] = 'AB'
970 metadata['ota-required-cache'] = '0'
971 else:
972 metadata['ota-type'] = 'BLOCK'
973
974 if OPTIONS.wipe_user_data:
975 metadata['ota-wipe'] = 'yes'
976
977 is_incremental = source_info is not None
978 if is_incremental:
979 metadata['pre-build'] = source_info.fingerprint
980 metadata['pre-build-incremental'] = source_info.GetBuildProp(
981 'ro.build.version.incremental')
982 metadata['pre-device'] = source_info.device
983 else:
984 metadata['pre-device'] = target_info.device
985
Tao Baofaa8e0b2018-04-12 14:31:43 -0700986 # Use the actual post-timestamp, even for a downgrade case.
987 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
988
989 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800990 if is_incremental:
991 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800992
993 return metadata
994
995
Tao Baod3fc38a2018-03-08 16:09:01 -0800996class PropertyFiles(object):
997 """A class that computes the property-files string for an OTA package.
998
999 A property-files string is a comma-separated string that contains the
1000 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1001 can be fetched directly with the package URL along with the offset/size info.
1002 These strings can be used for streaming A/B OTAs, or allowing an updater to
1003 download package metadata entry directly, without paying the cost of
1004 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001005
Tao Baocc8e2662018-03-01 19:30:00 -08001006 Computing the final property-files string requires two passes. Because doing
1007 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1008 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1009 values.
1010
1011 This class provides functions to be called for each pass. The general flow is
1012 as follows.
1013
Tao Baod3fc38a2018-03-08 16:09:01 -08001014 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001015 # The first pass, which writes placeholders before doing initial signing.
1016 property_files.Compute()
1017 SignOutput()
1018
1019 # The second pass, by replacing the placeholders with actual data.
1020 property_files.Finalize()
1021 SignOutput()
1022
1023 And the caller can additionally verify the final result.
1024
1025 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001026 """
1027
Tao Baocc8e2662018-03-01 19:30:00 -08001028 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001029 self.name = None
1030 self.required = ()
1031 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001032
Tao Baocc8e2662018-03-01 19:30:00 -08001033 def Compute(self, input_zip):
1034 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001035
Tao Baocc8e2662018-03-01 19:30:00 -08001036 We reserve extra space for the offset and size of the metadata entry itself,
1037 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001038
Tao Baocc8e2662018-03-01 19:30:00 -08001039 Args:
1040 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001041
Tao Baocc8e2662018-03-01 19:30:00 -08001042 Returns:
1043 A string with placeholders for the metadata offset/size info, e.g.
1044 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1045 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001046 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001047
Tao Baod2ce2ed2018-03-16 12:59:42 -07001048 class InsufficientSpaceException(Exception):
1049 pass
1050
Tao Baocc8e2662018-03-01 19:30:00 -08001051 def Finalize(self, input_zip, reserved_length):
1052 """Finalizes a property-files string with actual METADATA offset/size info.
1053
1054 The input ZIP file has been signed, with the ZIP entries in the desired
1055 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1056 the ZIP entry offsets and construct the property-files string with actual
1057 data. Note that during this process, we must pad the property-files string
1058 to the reserved length, so that the METADATA entry size remains the same.
1059 Otherwise the entries' offsets and sizes may change again.
1060
1061 Args:
1062 input_zip: The input ZIP file.
1063 reserved_length: The reserved length of the property-files string during
1064 the call to Compute(). The final string must be no more than this
1065 size.
1066
1067 Returns:
1068 A property-files string including the metadata offset/size info, e.g.
1069 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1070
1071 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001072 InsufficientSpaceException: If the reserved length is insufficient to hold
1073 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001074 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001075 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001076 if len(result) > reserved_length:
1077 raise self.InsufficientSpaceException(
1078 'Insufficient reserved space: reserved={}, actual={}'.format(
1079 reserved_length, len(result)))
1080
Tao Baocc8e2662018-03-01 19:30:00 -08001081 result += ' ' * (reserved_length - len(result))
1082 return result
1083
1084 def Verify(self, input_zip, expected):
1085 """Verifies the input ZIP file contains the expected property-files string.
1086
1087 Args:
1088 input_zip: The input ZIP file.
1089 expected: The property-files string that's computed from Finalize().
1090
1091 Raises:
1092 AssertionError: On finding a mismatch.
1093 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001094 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001095 assert actual == expected, \
1096 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1097
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001098 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1099 """
1100 Constructs the property-files string per request.
1101
1102 Args:
1103 zip_file: The input ZIP file.
1104 reserved_length: The reserved length of the property-files string.
1105
1106 Returns:
1107 A property-files string including the metadata offset/size info, e.g.
1108 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1109 """
Tao Baocc8e2662018-03-01 19:30:00 -08001110
1111 def ComputeEntryOffsetSize(name):
1112 """Computes the zip entry offset and size."""
1113 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001114 offset = info.header_offset
1115 offset += zipfile.sizeFileHeader
1116 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001117 size = info.file_size
1118 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1119
1120 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001121 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001122 for entry in self.required:
1123 tokens.append(ComputeEntryOffsetSize(entry))
1124 for entry in self.optional:
1125 if entry in zip_file.namelist():
1126 tokens.append(ComputeEntryOffsetSize(entry))
1127
1128 # 'META-INF/com/android/metadata' is required. We don't know its actual
1129 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001130 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1131 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1132 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1133 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001134 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001135 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001136 else:
1137 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1138
1139 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001140
Tao Bao85f16982018-03-08 16:28:33 -08001141 def _GetPrecomputed(self, input_zip):
1142 """Computes the additional tokens to be included into the property-files.
1143
1144 This applies to tokens without actual ZIP entries, such as
1145 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1146 that they can download the payload metadata directly with the info.
1147
1148 Args:
1149 input_zip: The input zip file.
1150
1151 Returns:
1152 A list of strings (tokens) to be added to the property-files string.
1153 """
1154 # pylint: disable=no-self-use
1155 # pylint: disable=unused-argument
1156 return []
1157
Tao Baofe5b69a2018-03-02 09:47:43 -08001158
Tao Baod3fc38a2018-03-08 16:09:01 -08001159class StreamingPropertyFiles(PropertyFiles):
1160 """A subclass for computing the property-files for streaming A/B OTAs."""
1161
1162 def __init__(self):
1163 super(StreamingPropertyFiles, self).__init__()
1164 self.name = 'ota-streaming-property-files'
1165 self.required = (
1166 # payload.bin and payload_properties.txt must exist.
1167 'payload.bin',
1168 'payload_properties.txt',
1169 )
1170 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001171 # care_map is available only if dm-verity is enabled.
1172 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001173 'care_map.txt',
1174 # compatibility.zip is available only if target supports Treble.
1175 'compatibility.zip',
1176 )
1177
1178
Tao Bao85f16982018-03-08 16:28:33 -08001179class AbOtaPropertyFiles(StreamingPropertyFiles):
1180 """The property-files for A/B OTA that includes payload_metadata.bin info.
1181
1182 Since P, we expose one more token (aka property-file), in addition to the ones
1183 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1184 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1185 doesn't exist as a separate ZIP entry, but can be used to verify if the
1186 payload can be applied on the given device.
1187
1188 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1189 and the newly added 'ota-property-files' in P. The new token will only be
1190 available in 'ota-property-files'.
1191 """
1192
1193 def __init__(self):
1194 super(AbOtaPropertyFiles, self).__init__()
1195 self.name = 'ota-property-files'
1196
1197 def _GetPrecomputed(self, input_zip):
1198 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1199 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1200
1201 @staticmethod
1202 def _GetPayloadMetadataOffsetAndSize(input_zip):
1203 """Computes the offset and size of the payload metadata for a given package.
1204
1205 (From system/update_engine/update_metadata.proto)
1206 A delta update file contains all the deltas needed to update a system from
1207 one specific version to another specific version. The update format is
1208 represented by this struct pseudocode:
1209
1210 struct delta_update_file {
1211 char magic[4] = "CrAU";
1212 uint64 file_format_version;
1213 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1214
1215 // Only present if format_version > 1:
1216 uint32 metadata_signature_size;
1217
1218 // The Bzip2 compressed DeltaArchiveManifest
1219 char manifest[metadata_signature_size];
1220
1221 // The signature of the metadata (from the beginning of the payload up to
1222 // this location, not including the signature itself). This is a
1223 // serialized Signatures message.
1224 char medatada_signature_message[metadata_signature_size];
1225
1226 // Data blobs for files, no specific format. The specific offset
1227 // and length of each data blob is recorded in the DeltaArchiveManifest.
1228 struct {
1229 char data[];
1230 } blobs[];
1231
1232 // These two are not signed:
1233 uint64 payload_signatures_message_size;
1234 char payload_signatures_message[];
1235 };
1236
1237 'payload-metadata.bin' contains all the bytes from the beginning of the
1238 payload, till the end of 'medatada_signature_message'.
1239 """
1240 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001241 payload_offset = payload_info.header_offset
1242 payload_offset += zipfile.sizeFileHeader
1243 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001244 payload_size = payload_info.file_size
1245
1246 with input_zip.open('payload.bin', 'r') as payload_fp:
1247 header_bin = payload_fp.read(24)
1248
1249 # network byte order (big-endian)
1250 header = struct.unpack("!IQQL", header_bin)
1251
1252 # 'CrAU'
1253 magic = header[0]
1254 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1255
1256 manifest_size = header[2]
1257 metadata_signature_size = header[3]
1258 metadata_total = 24 + manifest_size + metadata_signature_size
1259 assert metadata_total < payload_size
1260
1261 return (payload_offset, metadata_total)
1262
1263
Tao Bao491d7e22018-02-21 13:17:22 -08001264class NonAbOtaPropertyFiles(PropertyFiles):
1265 """The property-files for non-A/B OTA.
1266
1267 For non-A/B OTA, the property-files string contains the info for METADATA
1268 entry, with which a system updater can be fetched the package metadata prior
1269 to downloading the entire package.
1270 """
1271
1272 def __init__(self):
1273 super(NonAbOtaPropertyFiles, self).__init__()
1274 self.name = 'ota-property-files'
1275
1276
Tao Baod3fc38a2018-03-08 16:09:01 -08001277def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001278 """Finalizes the metadata and signs an A/B OTA package.
1279
1280 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1281 that contains the offsets and sizes for the ZIP entries. An example
1282 property-files string is as follows.
1283
1284 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1285
1286 OTA server can pass down this string, in addition to the package URL, to the
1287 system update client. System update client can then fetch individual ZIP
1288 entries (ZIP_STORED) directly at the given offset of the URL.
1289
1290 Args:
1291 metadata: The metadata dict for the package.
1292 input_file: The input ZIP filename that doesn't contain the package METADATA
1293 entry yet.
1294 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001295 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001296 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001297
Tao Baod2ce2ed2018-03-16 12:59:42 -07001298 def ComputeAllPropertyFiles(input_file, needed_property_files):
1299 # Write the current metadata entry with placeholders.
1300 with zipfile.ZipFile(input_file) as input_zip:
1301 for property_files in needed_property_files:
1302 metadata[property_files.name] = property_files.Compute(input_zip)
1303 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001304
Tao Baod2ce2ed2018-03-16 12:59:42 -07001305 if METADATA_NAME in namelist:
1306 common.ZipDelete(input_file, METADATA_NAME)
1307 output_zip = zipfile.ZipFile(input_file, 'a')
1308 WriteMetadata(metadata, output_zip)
1309 common.ZipClose(output_zip)
1310
1311 if OPTIONS.no_signing:
1312 return input_file
1313
Tao Bao491d7e22018-02-21 13:17:22 -08001314 prelim_signing = common.MakeTempFile(suffix='.zip')
1315 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001316 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001317
Tao Baod2ce2ed2018-03-16 12:59:42 -07001318 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1319 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1320 for property_files in needed_property_files:
1321 metadata[property_files.name] = property_files.Finalize(
1322 prelim_signing_zip, len(metadata[property_files.name]))
1323
1324 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1325 # entries, as well as padding the entry headers. We do a preliminary signing
1326 # (with an incomplete metadata entry) to allow that to happen. Then compute
1327 # the ZIP entry offsets, write back the final metadata and do the final
1328 # signing.
1329 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1330 try:
1331 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1332 except PropertyFiles.InsufficientSpaceException:
1333 # Even with the preliminary signing, the entry orders may change
1334 # dramatically, which leads to insufficiently reserved space during the
1335 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1336 # preliminary signing works, based on the already ordered ZIP entries, to
1337 # address the issue.
1338 prelim_signing = ComputeAllPropertyFiles(
1339 prelim_signing, needed_property_files)
1340 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001341
1342 # Replace the METADATA entry.
1343 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001344 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001345 WriteMetadata(metadata, output_zip)
1346 common.ZipClose(output_zip)
1347
1348 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001349 if OPTIONS.no_signing:
1350 output_file = prelim_signing
1351 else:
1352 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001353
1354 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001355 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001356 for property_files in needed_property_files:
1357 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001358
1359
Tao Bao491d7e22018-02-21 13:17:22 -08001360def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001361 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1362 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001363
Tao Bao481bab82017-12-21 11:23:09 -08001364 target_api_version = target_info["recovery_api_version"]
1365 source_api_version = source_info["recovery_api_version"]
1366 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001367 print("WARNING: generating edify script for a source that "
1368 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001369
Tao Bao481bab82017-12-21 11:23:09 -08001370 script = edify_generator.EdifyGenerator(
1371 source_api_version, target_info, fstab=source_info["fstab"])
1372
1373 if target_info.oem_props or source_info.oem_props:
1374 if not OPTIONS.oem_no_mount:
1375 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001376
Tao Baodf3a48b2018-01-10 16:30:43 -08001377 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001378
Tao Bao491d7e22018-02-21 13:17:22 -08001379 if not OPTIONS.no_signing:
1380 staging_file = common.MakeTempFile(suffix='.zip')
1381 else:
1382 staging_file = output_file
1383
1384 output_zip = zipfile.ZipFile(
1385 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1386
Geremy Condra36bd3652014-02-06 19:45:10 -08001387 device_specific = common.DeviceSpecificParams(
1388 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001389 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001390 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001391 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001392 output_zip=output_zip,
1393 script=script,
1394 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001395 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001396
Geremy Condra36bd3652014-02-06 19:45:10 -08001397 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001398 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001399 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001400 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001401 updating_boot = (not OPTIONS.two_step and
1402 (source_boot.data != target_boot.data))
1403
Geremy Condra36bd3652014-02-06 19:45:10 -08001404 target_recovery = common.GetBootableImage(
1405 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001406
Tao Baoe709b092018-02-07 12:40:00 -08001407 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1408 # shared blocks (i.e. some blocks will show up in multiple files' block
1409 # list). We can only allocate such shared blocks to the first "owner", and
1410 # disable imgdiff for all later occurrences.
1411 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1412 target_info.get('ext4_share_dup_blocks') == "true")
1413 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1414 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001415
1416 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1417 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001418 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001419 allow_shared_blocks,
1420 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001421
Tao Bao0582cb62017-12-21 11:47:01 -08001422 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001423 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001424 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001425
Tao Baof8acad12016-07-07 09:09:58 -07001426 # Check the first block of the source system partition for remount R/W only
1427 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001428 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001429 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001430 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1431 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1432 # b) the blocks listed in block map may not contain all the bytes for a given
1433 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001434 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001435 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1436 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001437 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001438 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001439 version=blockimgdiff_version,
1440 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001441
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001442 if HasVendorPartition(target_zip):
1443 if not HasVendorPartition(source_zip):
1444 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001445 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1446 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001447 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1448 "vendor", 4096, target_info)
1449 vendor_tgt = common.GetSparseImage(
1450 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1451 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001452
1453 # Check first block of vendor partition for remount R/W only if
1454 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001455 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001456 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001457 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001458 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001459 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001460 version=blockimgdiff_version,
1461 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001462 else:
1463 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001464
Tao Baobcd1d162017-08-26 13:10:26 -07001465 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001466 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001467
Tao Bao481bab82017-12-21 11:23:09 -08001468 # Assertions (e.g. device properties check).
1469 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001470 device_specific.IncrementalOTA_Assertions()
1471
1472 # Two-step incremental package strategy (in chronological order,
1473 # which is *not* the order in which the generated script has
1474 # things):
1475 #
1476 # if stage is not "2/3" or "3/3":
1477 # do verification on current system
1478 # write recovery image to boot partition
1479 # set stage to "2/3"
1480 # reboot to boot partition and restart recovery
1481 # else if stage is "2/3":
1482 # write recovery image to recovery partition
1483 # set stage to "3/3"
1484 # reboot to recovery partition and restart recovery
1485 # else:
1486 # (stage must be "3/3")
1487 # perform update:
1488 # patch system files, etc.
1489 # force full install of new boot image
1490 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001491 # complete script normally
1492 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001493
1494 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001495 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001496 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001497 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001498 assert fs.fs_type.upper() == "EMMC", \
1499 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001500 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001501 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1502 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001503if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001504""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001505
1506 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1507 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001508 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001509 script.WriteRawImage("/recovery", "recovery.img")
1510 script.AppendExtra("""
1511set_stage("%(bcb_dev)s", "3/3");
1512reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001513else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001514""" % bcb_dev)
1515
Tao Baod42e97e2016-11-30 12:11:57 -08001516 # Stage 1/3: (a) Verify the current system.
1517 script.Comment("Stage 1/3")
1518
Tao Bao6c55a8a2015-04-08 15:30:27 -07001519 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001520 script.Print("Source: {}".format(source_info.fingerprint))
1521 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001522
Geremy Condra36bd3652014-02-06 19:45:10 -08001523 script.Print("Verifying current system...")
1524
1525 device_specific.IncrementalOTA_VerifyBegin()
1526
Tao Bao481bab82017-12-21 11:23:09 -08001527 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001528
Tao Baod8d14be2016-02-04 14:26:02 -08001529 # Check the required cache size (i.e. stashed blocks).
1530 size = []
1531 if system_diff:
1532 size.append(system_diff.required_cache)
1533 if vendor_diff:
1534 size.append(vendor_diff.required_cache)
1535
Geremy Condra36bd3652014-02-06 19:45:10 -08001536 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001537 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001538 d = common.Difference(target_boot, source_boot)
1539 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001540 if d is None:
1541 include_full_boot = True
1542 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1543 else:
1544 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001545
Tao Bao89fbb0f2017-01-10 10:47:58 -08001546 print("boot target: %d source: %d diff: %d" % (
1547 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001548
Tao Bao51216552018-08-26 11:53:15 -07001549 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001550
Tao Bao51216552018-08-26 11:53:15 -07001551 script.PatchPartitionCheck(
1552 "{}:{}:{}:{}".format(
1553 boot_type, boot_device, target_boot.size, target_boot.sha1),
1554 "{}:{}:{}:{}".format(
1555 boot_type, boot_device, source_boot.size, source_boot.sha1))
1556
Tao Baod8d14be2016-02-04 14:26:02 -08001557 size.append(target_boot.size)
1558
1559 if size:
1560 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001561
1562 device_specific.IncrementalOTA_VerifyEnd()
1563
1564 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001565 # Stage 1/3: (b) Write recovery image to /boot.
1566 _WriteRecoveryImageToBoot(script, output_zip)
1567
Geremy Condra36bd3652014-02-06 19:45:10 -08001568 script.AppendExtra("""
1569set_stage("%(bcb_dev)s", "2/3");
1570reboot_now("%(bcb_dev)s", "");
1571else
1572""" % bcb_dev)
1573
Tao Baod42e97e2016-11-30 12:11:57 -08001574 # Stage 3/3: Make changes.
1575 script.Comment("Stage 3/3")
1576
Jesse Zhao75bcea02015-01-06 10:59:53 -08001577 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001578 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001579 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001580 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001581
Geremy Condra36bd3652014-02-06 19:45:10 -08001582 script.Comment("---- start making changes here ----")
1583
1584 device_specific.IncrementalOTA_InstallBegin()
1585
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001586 system_diff.WriteScript(script, output_zip,
Tao Bao76def242017-11-21 09:25:31 -08001587 progress=0.8 if vendor_diff else 0.9,
1588 write_verify_script=OPTIONS.verify)
Tao Bao68658c02015-06-01 13:40:49 -07001589
Doug Zongkerfc44a512014-08-26 13:10:25 -07001590 if vendor_diff:
Tao Bao76def242017-11-21 09:25:31 -08001591 vendor_diff.WriteScript(script, output_zip, progress=0.1,
1592 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001593
1594 if OPTIONS.two_step:
1595 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1596 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001597 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001598
1599 if not OPTIONS.two_step:
1600 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001601 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001602 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001603 script.Print("Installing boot image...")
1604 script.WriteRawImage("/boot", "boot.img")
1605 else:
1606 # Produce the boot image by applying a patch to the current
1607 # contents of the boot partition, and write it back to the
1608 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001609 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001610 script.Print("Patching boot image...")
1611 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001612 script.PatchPartition(
1613 '{}:{}:{}:{}'.format(
1614 boot_type, boot_device, target_boot.size, target_boot.sha1),
1615 '{}:{}:{}:{}'.format(
1616 boot_type, boot_device, source_boot.size, source_boot.sha1),
1617 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001618 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001619 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001620
1621 # Do device-specific installation (eg, write radio image).
1622 device_specific.IncrementalOTA_InstallEnd()
1623
1624 if OPTIONS.extra_script is not None:
1625 script.AppendExtra(OPTIONS.extra_script)
1626
Doug Zongker922206e2014-03-04 13:16:24 -08001627 if OPTIONS.wipe_user_data:
1628 script.Print("Erasing user data...")
1629 script.FormatPartition("/data")
1630
Geremy Condra36bd3652014-02-06 19:45:10 -08001631 if OPTIONS.two_step:
1632 script.AppendExtra("""
1633set_stage("%(bcb_dev)s", "");
1634endif;
1635endif;
1636""" % bcb_dev)
1637
1638 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001639 # For downgrade OTAs, we prefer to use the update-binary in the source
1640 # build that is actually newer than the one in the target build.
1641 if OPTIONS.downgrade:
1642 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1643 else:
1644 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001645 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001646
1647 # We haven't written the metadata entry yet, which will be handled in
1648 # FinalizeMetadata().
1649 common.ZipClose(output_zip)
1650
1651 # Sign the generated zip package unless no_signing is specified.
1652 needed_property_files = (
1653 NonAbOtaPropertyFiles(),
1654 )
1655 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001656
Doug Zongker32b527d2014-03-04 10:03:02 -08001657
Tao Bao15a146a2018-02-21 16:06:59 -08001658def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001659 """Returns a target-files.zip file for generating secondary payload.
1660
1661 Although the original target-files.zip already contains secondary slot
1662 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1663 ones without _other suffix. Note that we cannot instead modify the names in
1664 META/ab_partitions.txt, because there are no matching partitions on device.
1665
1666 For the partitions that don't have secondary images, the ones for primary
1667 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1668 bootloader images in the inactive slot.
1669
1670 Args:
1671 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001672 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001673
1674 Returns:
1675 The filename of the target-files.zip for generating secondary payload.
1676 """
1677 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1678 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1679
Tao Baodba59ee2018-01-09 13:21:02 -08001680 with zipfile.ZipFile(input_file, 'r') as input_zip:
1681 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001682 namelist = input_zip.namelist()
1683
1684 # Additionally unzip 'RADIO/*' if exists.
1685 unzip_pattern = UNZIP_PATTERN[:]
1686 if any([entry.startswith('RADIO/') for entry in namelist]):
1687 unzip_pattern.append('RADIO/*')
1688 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001689
1690 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001691 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1692 if info.filename == 'IMAGES/system_other.img':
1693 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1694
1695 # Primary images and friends need to be skipped explicitly.
1696 elif info.filename in ('IMAGES/system.img',
1697 'IMAGES/system.map'):
1698 pass
1699
Tao Bao15a146a2018-02-21 16:06:59 -08001700 # Skip copying the postinstall config if requested.
1701 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1702 pass
1703
Tao Bao12489802018-07-12 14:47:38 -07001704 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001705 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1706
Tao Baof7140c02018-01-30 17:09:24 -08001707 common.ZipClose(target_zip)
1708
1709 return target_file
1710
1711
Tao Bao15a146a2018-02-21 16:06:59 -08001712def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1713 """Returns a target-files.zip that's not containing postinstall_config.txt.
1714
1715 This allows brillo_update_payload script to skip writing all the postinstall
1716 hooks in the generated payload. The input target-files.zip file will be
1717 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1718 contain the postinstall_config.txt entry, the input file will be returned.
1719
1720 Args:
1721 input_file: The input target-files.zip filename.
1722
1723 Returns:
1724 The filename of target-files.zip that doesn't contain postinstall config.
1725 """
1726 # We should only make a copy if postinstall_config entry exists.
1727 with zipfile.ZipFile(input_file, 'r') as input_zip:
1728 if POSTINSTALL_CONFIG not in input_zip.namelist():
1729 return input_file
1730
1731 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1732 shutil.copyfile(input_file, target_file)
1733 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1734 return target_file
1735
1736
Tao Baoc098e9e2016-01-07 13:03:56 -08001737def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1738 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001739 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001740 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001741 if not OPTIONS.no_signing:
1742 staging_file = common.MakeTempFile(suffix='.zip')
1743 else:
1744 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001745 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001746 compression=zipfile.ZIP_DEFLATED)
1747
Tao Bao481bab82017-12-21 11:23:09 -08001748 if source_file is not None:
1749 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1750 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1751 else:
1752 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1753 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001754
Tao Bao481bab82017-12-21 11:23:09 -08001755 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001756 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001757
Tao Bao15a146a2018-02-21 16:06:59 -08001758 if OPTIONS.skip_postinstall:
1759 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1760
Tao Bao40b18822018-01-30 18:19:04 -08001761 # Generate payload.
1762 payload = Payload()
1763
1764 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001765 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001766 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001767 else:
1768 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001769 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001770
Tao Bao40b18822018-01-30 18:19:04 -08001771 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001772
Tao Bao40b18822018-01-30 18:19:04 -08001773 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001774 payload_signer = PayloadSigner()
1775 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001776
Tao Bao40b18822018-01-30 18:19:04 -08001777 # Write the payload into output zip.
1778 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001779
Tao Baof7140c02018-01-30 17:09:24 -08001780 # Generate and include the secondary payload that installs secondary images
1781 # (e.g. system_other.img).
1782 if OPTIONS.include_secondary:
1783 # We always include a full payload for the secondary slot, even when
1784 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001785 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1786 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001787 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001788 secondary_payload.Generate(secondary_target_file,
1789 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001790 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001791 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001792
Tianjie Xucfa86222016-03-07 16:31:19 -08001793 # If dm-verity is supported for the device, copy contents of care_map
1794 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001795 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001796 if (target_info.get("verity") == "true" or
1797 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001798 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1799 "META/" + x in target_zip.namelist()]
1800
1801 # Adds care_map if either the protobuf format or the plain text one exists.
1802 if care_map_list:
1803 care_map_name = care_map_list[0]
1804 care_map_data = target_zip.read("META/" + care_map_name)
1805 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001806 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001807 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001808 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001809 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001810 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001811
Tao Baobcd1d162017-08-26 13:10:26 -07001812 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001813 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001814
Tao Bao21803d32017-04-19 10:16:09 -07001815 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001816
Tao Baofe5b69a2018-03-02 09:47:43 -08001817 # We haven't written the metadata entry yet, which will be handled in
1818 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001819 common.ZipClose(output_zip)
1820
Tao Bao85f16982018-03-08 16:28:33 -08001821 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1822 # all the info of the latter. However, system updaters and OTA servers need to
1823 # take time to switch to the new flag. We keep both of the flags for
1824 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001825 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001826 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001827 StreamingPropertyFiles(),
1828 )
1829 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001830
Tao Baoc098e9e2016-01-07 13:03:56 -08001831
Doug Zongkereef39442009-04-02 12:14:19 -07001832def main(argv):
1833
1834 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001835 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001836 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001837 elif o in ("-i", "--incremental_from"):
1838 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001839 elif o == "--full_radio":
1840 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001841 elif o == "--full_bootloader":
1842 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001843 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001844 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001845 elif o == "--downgrade":
1846 OPTIONS.downgrade = True
1847 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001848 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001849 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001850 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001851 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001852 elif o == "--oem_no_mount":
1853 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001854 elif o in ("-e", "--extra_script"):
1855 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001856 elif o in ("-t", "--worker_threads"):
1857 if a.isdigit():
1858 OPTIONS.worker_threads = int(a)
1859 else:
1860 raise ValueError("Cannot parse value %r for option %r - only "
1861 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001862 elif o in ("-2", "--two_step"):
1863 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001864 elif o == "--include_secondary":
1865 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001866 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001867 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001868 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001869 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001870 elif o == "--block":
1871 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001872 elif o in ("-b", "--binary"):
1873 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001874 elif o == "--stash_threshold":
1875 try:
1876 OPTIONS.stash_threshold = float(a)
1877 except ValueError:
1878 raise ValueError("Cannot parse value %r for option %r - expecting "
1879 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001880 elif o == "--log_diff":
1881 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001882 elif o == "--payload_signer":
1883 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001884 elif o == "--payload_signer_args":
1885 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001886 elif o == "--extracted_input_target_files":
1887 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001888 elif o == "--skip_postinstall":
1889 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001890 else:
1891 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001892 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001893
1894 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001895 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001896 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001897 "package_key=",
1898 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001899 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001900 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001901 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001902 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001903 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001904 "extra_script=",
1905 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001906 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001907 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001908 "no_signing",
1909 "block",
1910 "binary=",
1911 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001912 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001913 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001914 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001915 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001916 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001917 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001918 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001919 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001920 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001921
1922 if len(args) != 2:
1923 common.Usage(__doc__)
1924 sys.exit(1)
1925
Tao Bao5d182562016-02-23 11:38:39 -08001926 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08001927 # We should only allow downgrading incrementals (as opposed to full).
1928 # Otherwise the device may go back from arbitrary build with this full
1929 # OTA package.
1930 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001931 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001932
Tao Bao2db13852018-01-08 22:28:57 -08001933 # Load the build info dicts from the zip directly or the extracted input
1934 # directory. We don't need to unzip the entire target-files zips, because they
1935 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1936 # When loading the info dicts, we don't need to provide the second parameter
1937 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1938 # some properties with their actual paths, such as 'selinux_fc',
1939 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001940 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001941 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001942 else:
Tao Bao2db13852018-01-08 22:28:57 -08001943 with zipfile.ZipFile(args[0], 'r') as input_zip:
1944 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001945
Tao Bao2db13852018-01-08 22:28:57 -08001946 if OPTIONS.verbose:
1947 print("--- target info ---")
1948 common.DumpInfoDict(OPTIONS.info_dict)
1949
1950 # Load the source build dict if applicable.
1951 if OPTIONS.incremental_source is not None:
1952 OPTIONS.target_info_dict = OPTIONS.info_dict
1953 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1954 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1955
1956 if OPTIONS.verbose:
1957 print("--- source info ---")
1958 common.DumpInfoDict(OPTIONS.source_info_dict)
1959
1960 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001961 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1962
Tao Baoc098e9e2016-01-07 13:03:56 -08001963 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1964
Christian Oderf63e2cd2017-05-01 22:30:15 +02001965 # Use the default key to sign the package if not specified with package_key.
1966 # package_keys are needed on ab_updates, so always define them if an
1967 # ab_update is getting created.
1968 if not OPTIONS.no_signing or ab_update:
1969 if OPTIONS.package_key is None:
1970 OPTIONS.package_key = OPTIONS.info_dict.get(
1971 "default_system_dev_certificate",
1972 "build/target/product/security/testkey")
1973 # Get signing keys
1974 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1975
Tao Baoc098e9e2016-01-07 13:03:56 -08001976 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001977 WriteABOTAPackageWithBrilloScript(
1978 target_file=args[0],
1979 output_file=args[1],
1980 source_file=OPTIONS.incremental_source)
1981
Tao Bao89fbb0f2017-01-10 10:47:58 -08001982 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001983 return
1984
Tao Bao2db13852018-01-08 22:28:57 -08001985 # Sanity check the loaded info dicts first.
1986 if OPTIONS.info_dict.get("no_recovery") == "true":
1987 raise common.ExternalError(
1988 "--- target build has specified no recovery ---")
1989
1990 # Non-A/B OTAs rely on /cache partition to store temporary files.
1991 cache_size = OPTIONS.info_dict.get("cache_size")
1992 if cache_size is None:
1993 print("--- can't determine the cache partition size ---")
1994 OPTIONS.cache_size = cache_size
1995
Doug Zongker1c390a22009-05-14 19:06:36 -07001996 if OPTIONS.extra_script is not None:
1997 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1998
Dan Willemsencea5cd22017-03-21 14:44:27 -07001999 if OPTIONS.extracted_input is not None:
2000 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002001 else:
2002 print("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002003 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002004 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002005
Tao Bao2db13852018-01-08 22:28:57 -08002006 # If the caller explicitly specified the device-specific extensions path via
2007 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2008 # is present in the target target_files. Otherwise, take the path of the file
2009 # from 'tool_extensions' in the info dict and look for that in the local
2010 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002011 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002012 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2013 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002014 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002015 OPTIONS.device_specific = from_input
2016 else:
Tao Bao2db13852018-01-08 22:28:57 -08002017 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002018
Doug Zongker37974732010-09-16 17:44:38 -07002019 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002020 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002021
Tao Bao767e3ac2015-11-10 12:19:19 -08002022 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002023 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002024 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002025 WriteFullOTAPackage(
2026 input_zip,
2027 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002028
Tao Bao32b80dc2018-01-08 22:50:47 -08002029 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002030 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002031 print("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002032 OPTIONS.source_tmp = common.UnzipTemp(
2033 OPTIONS.incremental_source, UNZIP_PATTERN)
2034 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2035 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002036 WriteBlockIncrementalOTAPackage(
2037 input_zip,
2038 source_zip,
2039 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002040
2041 if OPTIONS.log_diff:
2042 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002043 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002044 target_files_diff.recursiveDiff(
2045 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002046
Tao Bao89fbb0f2017-01-10 10:47:58 -08002047 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002048
2049
2050if __name__ == '__main__':
2051 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002052 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002053 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002054 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002055 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002056 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002057 finally:
2058 common.Cleanup()