blob: 2264655b2e99ccabdb8c9a99a77b4893204da854 [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
Tao Bao32fcdab2018-10-12 10:30:39 -0700167import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700168import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800169import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700170import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800171import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800172import struct
Tao Bao481bab82017-12-21 11:23:09 -0800173import 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
Tao Bao32fcdab2018-10-12 10:30:39 -0700185logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800186
Doug Zongkereef39442009-04-02 12:14:19 -0700187OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700188OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700189OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700190OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700191OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700192OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800193OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700194OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700195OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
196if OPTIONS.worker_threads == 0:
197 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800198OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800199OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900200OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800201OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800202OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700203OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800204OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700205OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700206OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700207# Stash size cannot exceed cache_size * threshold.
208OPTIONS.cache_size = None
209OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800210OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700211OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700212OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700213OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200214OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800215OPTIONS.skip_postinstall = False
216
Tao Bao8dcf7382015-05-21 14:09:49 -0700217
Tao Bao2dd1c482017-02-03 16:49:39 -0800218METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800219POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800220UNZIP_PATTERN = ['IMAGES/*', 'META/*']
221
Tao Bao2dd1c482017-02-03 16:49:39 -0800222
Tao Bao481bab82017-12-21 11:23:09 -0800223class BuildInfo(object):
224 """A class that holds the information for a given build.
225
226 This class wraps up the property querying for a given source or target build.
227 It abstracts away the logic of handling OEM-specific properties, and caches
228 the commonly used properties such as fingerprint.
229
230 There are two types of info dicts: a) build-time info dict, which is generated
231 at build time (i.e. included in a target_files zip); b) OEM info dict that is
232 specified at package generation time (via command line argument
233 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
234 having "oem_fingerprint_properties" in build-time info dict), all the queries
235 would be answered based on build-time info dict only. Otherwise if using
236 OEM-specific properties, some of them will be calculated from two info dicts.
237
238 Users can query properties similarly as using a dict() (e.g. info['fstab']),
239 or to query build properties via GetBuildProp() or GetVendorBuildProp().
240
241 Attributes:
242 info_dict: The build-time info dict.
243 is_ab: Whether it's a build that uses A/B OTA.
244 oem_dicts: A list of OEM dicts.
245 oem_props: A list of OEM properties that should be read from OEM dicts; None
246 if the build doesn't use any OEM-specific property.
247 fingerprint: The fingerprint of the build, which would be calculated based
248 on OEM properties if applicable.
249 device: The device name, which could come from OEM dicts if applicable.
250 """
251
252 def __init__(self, info_dict, oem_dicts):
253 """Initializes a BuildInfo instance with the given dicts.
254
Tao Bao667c7532018-07-06 10:13:59 -0700255 Note that it only wraps up the given dicts, without making copies.
256
Tao Bao481bab82017-12-21 11:23:09 -0800257 Arguments:
258 info_dict: The build-time info dict.
259 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
260 that it always uses the first dict to calculate the fingerprint or the
261 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700262 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800263 """
264 self.info_dict = info_dict
265 self.oem_dicts = oem_dicts
266
267 self._is_ab = info_dict.get("ab_update") == "true"
268 self._oem_props = info_dict.get("oem_fingerprint_properties")
269
270 if self._oem_props:
271 assert oem_dicts, "OEM source required for this build"
272
273 # These two should be computed only after setting self._oem_props.
274 self._device = self.GetOemProperty("ro.product.device")
275 self._fingerprint = self.CalculateFingerprint()
276
277 @property
278 def is_ab(self):
279 return self._is_ab
280
281 @property
282 def device(self):
283 return self._device
284
285 @property
286 def fingerprint(self):
287 return self._fingerprint
288
289 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700290 def vendor_fingerprint(self):
291 if "vendor.build.prop" not in self.info_dict:
292 return None
293 vendor_build_prop = self.info_dict["vendor.build.prop"]
294 if "ro.vendor.build.fingerprint" in vendor_build_prop:
295 return vendor_build_prop["ro.vendor.build.fingerprint"]
296 if "ro.vendor.build.thumbprint" in vendor_build_prop:
297 return vendor_build_prop["ro.vendor.build.thumbprint"]
298 return None
299
300 @property
Tao Bao481bab82017-12-21 11:23:09 -0800301 def oem_props(self):
302 return self._oem_props
303
304 def __getitem__(self, key):
305 return self.info_dict[key]
306
Tao Bao667c7532018-07-06 10:13:59 -0700307 def __setitem__(self, key, value):
308 self.info_dict[key] = value
309
Tao Bao481bab82017-12-21 11:23:09 -0800310 def get(self, key, default=None):
311 return self.info_dict.get(key, default)
312
Tao Bao667c7532018-07-06 10:13:59 -0700313 def items(self):
314 return self.info_dict.items()
315
Tao Bao481bab82017-12-21 11:23:09 -0800316 def GetBuildProp(self, prop):
317 """Returns the inquired build property."""
318 try:
319 return self.info_dict.get("build.prop", {})[prop]
320 except KeyError:
321 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
322
323 def GetVendorBuildProp(self, prop):
324 """Returns the inquired vendor build property."""
325 try:
326 return self.info_dict.get("vendor.build.prop", {})[prop]
327 except KeyError:
328 raise common.ExternalError(
329 "couldn't find %s in vendor.build.prop" % (prop,))
330
331 def GetOemProperty(self, key):
332 if self.oem_props is not None and key in self.oem_props:
333 return self.oem_dicts[0][key]
334 return self.GetBuildProp(key)
335
336 def CalculateFingerprint(self):
337 if self.oem_props is None:
338 return self.GetBuildProp("ro.build.fingerprint")
339 return "%s/%s/%s:%s" % (
340 self.GetOemProperty("ro.product.brand"),
341 self.GetOemProperty("ro.product.name"),
342 self.GetOemProperty("ro.product.device"),
343 self.GetBuildProp("ro.build.thumbprint"))
344
345 def WriteMountOemScript(self, script):
346 assert self.oem_props is not None
347 recovery_mount_options = self.info_dict.get("recovery_mount_options")
348 script.Mount("/oem", recovery_mount_options)
349
350 def WriteDeviceAssertions(self, script, oem_no_mount):
351 # Read the property directly if not using OEM properties.
352 if not self.oem_props:
353 script.AssertDevice(self.device)
354 return
355
356 # Otherwise assert OEM properties.
357 if not self.oem_dicts:
358 raise common.ExternalError(
359 "No OEM file provided to answer expected assertions")
360
361 for prop in self.oem_props.split():
362 values = []
363 for oem_dict in self.oem_dicts:
364 if prop in oem_dict:
365 values.append(oem_dict[prop])
366 if not values:
367 raise common.ExternalError(
368 "The OEM file is missing the property %s" % (prop,))
369 script.AssertOemProperty(prop, values, oem_no_mount)
370
371
Tao Baofabe0832018-01-17 15:52:28 -0800372class PayloadSigner(object):
373 """A class that wraps the payload signing works.
374
375 When generating a Payload, hashes of the payload and metadata files will be
376 signed with the device key, either by calling an external payload signer or
377 by calling openssl with the package key. This class provides a unified
378 interface, so that callers can just call PayloadSigner.Sign().
379
380 If an external payload signer has been specified (OPTIONS.payload_signer), it
381 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
382 that the signing key should be provided as part of the payload_signer_args.
383 Otherwise without an external signer, it uses the package key
384 (OPTIONS.package_key) and calls openssl for the signing works.
385 """
386
387 def __init__(self):
388 if OPTIONS.payload_signer is None:
389 # Prepare the payload signing key.
390 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
391 pw = OPTIONS.key_passwords[OPTIONS.package_key]
392
393 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
394 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
395 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
396 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700397 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800398
399 self.signer = "openssl"
400 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
401 "-pkeyopt", "digest:sha256"]
402 else:
403 self.signer = OPTIONS.payload_signer
404 self.signer_args = OPTIONS.payload_signer_args
405
406 def Sign(self, in_file):
407 """Signs the given input file. Returns the output filename."""
408 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
409 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700410 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800411 return out_file
412
413
Tao Bao40b18822018-01-30 18:19:04 -0800414class Payload(object):
415 """Manages the creation and the signing of an A/B OTA Payload."""
416
417 PAYLOAD_BIN = 'payload.bin'
418 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800419 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
420 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800421
Tao Bao667ff572018-02-10 00:02:40 -0800422 def __init__(self, secondary=False):
423 """Initializes a Payload instance.
424
425 Args:
426 secondary: Whether it's generating a secondary payload (default: False).
427 """
Tao Bao40b18822018-01-30 18:19:04 -0800428 self.payload_file = None
429 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800430 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800431
432 def Generate(self, target_file, source_file=None, additional_args=None):
433 """Generates a payload from the given target-files zip(s).
434
435 Args:
436 target_file: The filename of the target build target-files zip.
437 source_file: The filename of the source build target-files zip; or None if
438 generating a full OTA.
439 additional_args: A list of additional args that should be passed to
440 brillo_update_payload script; or None.
441 """
442 if additional_args is None:
443 additional_args = []
444
445 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
446 cmd = ["brillo_update_payload", "generate",
447 "--payload", payload_file,
448 "--target_image", target_file]
449 if source_file is not None:
450 cmd.extend(["--source_image", source_file])
451 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700452 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800453
454 self.payload_file = payload_file
455 self.payload_properties = None
456
457 def Sign(self, payload_signer):
458 """Generates and signs the hashes of the payload and metadata.
459
460 Args:
461 payload_signer: A PayloadSigner() instance that serves the signing work.
462
463 Raises:
464 AssertionError: On any failure when calling brillo_update_payload script.
465 """
466 assert isinstance(payload_signer, PayloadSigner)
467
468 # 1. Generate hashes of the payload and metadata files.
469 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
470 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
471 cmd = ["brillo_update_payload", "hash",
472 "--unsigned_payload", self.payload_file,
473 "--signature_size", "256",
474 "--metadata_hash_file", metadata_sig_file,
475 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700476 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800477
478 # 2. Sign the hashes.
479 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
480 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
481
482 # 3. Insert the signatures back into the payload file.
483 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
484 suffix=".bin")
485 cmd = ["brillo_update_payload", "sign",
486 "--unsigned_payload", self.payload_file,
487 "--payload", signed_payload_file,
488 "--signature_size", "256",
489 "--metadata_signature_file", signed_metadata_sig_file,
490 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700491 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800492
493 # 4. Dump the signed payload properties.
494 properties_file = common.MakeTempFile(prefix="payload-properties-",
495 suffix=".txt")
496 cmd = ["brillo_update_payload", "properties",
497 "--payload", signed_payload_file,
498 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700499 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800500
Tao Bao667ff572018-02-10 00:02:40 -0800501 if self.secondary:
502 with open(properties_file, "a") as f:
503 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
504
Tao Bao40b18822018-01-30 18:19:04 -0800505 if OPTIONS.wipe_user_data:
506 with open(properties_file, "a") as f:
507 f.write("POWERWASH=1\n")
508
509 self.payload_file = signed_payload_file
510 self.payload_properties = properties_file
511
Tao Bao667ff572018-02-10 00:02:40 -0800512 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800513 """Writes the payload to the given zip.
514
515 Args:
516 output_zip: The output ZipFile instance.
517 """
518 assert self.payload_file is not None
519 assert self.payload_properties is not None
520
Tao Bao667ff572018-02-10 00:02:40 -0800521 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800522 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
523 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
524 else:
525 payload_arcname = Payload.PAYLOAD_BIN
526 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
527
Tao Bao40b18822018-01-30 18:19:04 -0800528 # Add the signed payload file and properties into the zip. In order to
529 # support streaming, we pack them as ZIP_STORED. So these entries can be
530 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800531 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800532 compress_type=zipfile.ZIP_STORED)
533 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800534 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800535 compress_type=zipfile.ZIP_STORED)
536
537
Doug Zongkereef39442009-04-02 12:14:19 -0700538def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200539 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700540
Doug Zongker951495f2009-08-14 12:44:19 -0700541 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
542 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700543
544
Tao Bao481bab82017-12-21 11:23:09 -0800545def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800546 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800547 if not oem_source:
548 return None
549
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800550 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800551 for oem_file in oem_source:
552 with open(oem_file) as fp:
553 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800554 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700555
Doug Zongkereef39442009-04-02 12:14:19 -0700556
Tao Baod42e97e2016-11-30 12:11:57 -0800557def _WriteRecoveryImageToBoot(script, output_zip):
558 """Find and write recovery image to /boot in two-step OTA.
559
560 In two-step OTAs, we write recovery image to /boot as the first step so that
561 we can reboot to there and install a new recovery image to /recovery.
562 A special "recovery-two-step.img" will be preferred, which encodes the correct
563 path of "/boot". Otherwise the device may show "device is corrupt" message
564 when booting into /boot.
565
566 Fall back to using the regular recovery.img if the two-step recovery image
567 doesn't exist. Note that rebuilding the special image at this point may be
568 infeasible, because we don't have the desired boot signer and keys when
569 calling ota_from_target_files.py.
570 """
571
572 recovery_two_step_img_name = "recovery-two-step.img"
573 recovery_two_step_img_path = os.path.join(
574 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
575 if os.path.exists(recovery_two_step_img_path):
576 recovery_two_step_img = common.GetBootableImage(
577 recovery_two_step_img_name, recovery_two_step_img_name,
578 OPTIONS.input_tmp, "RECOVERY")
579 common.ZipWriteStr(
580 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700581 logger.info(
582 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800583 script.WriteRawImage("/boot", recovery_two_step_img_name)
584 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700585 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800586 # The "recovery.img" entry has been written into package earlier.
587 script.WriteRawImage("/boot", "recovery.img")
588
589
Doug Zongkerc9253822014-02-04 12:17:58 -0800590def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700591 namelist = [name for name in target_files_zip.namelist()]
592 return ("SYSTEM/recovery-from-boot.p" in namelist or
593 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700594
Tao Bao457cbf62017-03-06 09:56:01 -0800595
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700596def HasVendorPartition(target_files_zip):
597 try:
598 target_files_zip.getinfo("VENDOR/")
599 return True
600 except KeyError:
601 return False
602
Tao Bao457cbf62017-03-06 09:56:01 -0800603
Tao Bao481bab82017-12-21 11:23:09 -0800604def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700605 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800606 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700607
608
Tao Bao481bab82017-12-21 11:23:09 -0800609def WriteFingerprintAssertion(script, target_info, source_info):
610 source_oem_props = source_info.oem_props
611 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700612
Tao Bao481bab82017-12-21 11:23:09 -0800613 if source_oem_props is None and target_oem_props is None:
614 script.AssertSomeFingerprint(
615 source_info.fingerprint, target_info.fingerprint)
616 elif source_oem_props is not None and target_oem_props is not None:
617 script.AssertSomeThumbprint(
618 target_info.GetBuildProp("ro.build.thumbprint"),
619 source_info.GetBuildProp("ro.build.thumbprint"))
620 elif source_oem_props is None and target_oem_props is not None:
621 script.AssertFingerprintOrThumbprint(
622 source_info.fingerprint,
623 target_info.GetBuildProp("ro.build.thumbprint"))
624 else:
625 script.AssertFingerprintOrThumbprint(
626 target_info.fingerprint,
627 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700628
Doug Zongkerfc44a512014-08-26 13:10:25 -0700629
Tao Bao481bab82017-12-21 11:23:09 -0800630def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
631 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700632 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700633
634 Metadata used for on-device compatibility verification is retrieved from
635 target_zip then added to compatibility.zip which is added to the output_zip
636 archive.
637
Tao Baobcd1d162017-08-26 13:10:26 -0700638 Compatibility archive should only be included for devices that have enabled
639 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700640
641 Args:
642 target_zip: Zip file containing the source files to be included for OTA.
643 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800644 target_info: The BuildInfo instance that holds the target build info.
645 source_info: The BuildInfo instance that holds the source build info, if
646 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700647 """
648
Tao Baobcd1d162017-08-26 13:10:26 -0700649 def AddCompatibilityArchive(system_updated, vendor_updated):
650 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700651
Tao Baobcd1d162017-08-26 13:10:26 -0700652 Args:
653 system_updated: If True, the system image will be updated and therefore
654 its metadata should be included.
655 vendor_updated: If True, the vendor image will be updated and therefore
656 its metadata should be included.
657 """
658 # Determine what metadata we need. Files are names relative to META/.
659 compatibility_files = []
660 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
661 system_metadata = ("system_manifest.xml", "system_matrix.xml")
662 if vendor_updated:
663 compatibility_files += vendor_metadata
664 if system_updated:
665 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700666
Tao Baobcd1d162017-08-26 13:10:26 -0700667 # Create new archive.
668 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800669 compatibility_archive_zip = zipfile.ZipFile(
670 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700671
Tao Baobcd1d162017-08-26 13:10:26 -0700672 # Add metadata.
673 for file_name in compatibility_files:
674 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700675
Tao Baobcd1d162017-08-26 13:10:26 -0700676 if target_file_name in target_zip.namelist():
677 data = target_zip.read(target_file_name)
678 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700679
Tao Baobcd1d162017-08-26 13:10:26 -0700680 # Ensure files are written before we copy into output_zip.
681 compatibility_archive_zip.close()
682
683 # Only add the archive if we have any compatibility info.
684 if compatibility_archive_zip.namelist():
685 common.ZipWrite(output_zip, compatibility_archive.name,
686 arcname="compatibility.zip",
687 compress_type=zipfile.ZIP_STORED)
688
689 # Will only proceed if the target has enabled the Treble support (as well as
690 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800691 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700692 return
693
Tao Baobcd1d162017-08-26 13:10:26 -0700694 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800695 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700696 AddCompatibilityArchive(True, True)
697 return
698
Tao Bao481bab82017-12-21 11:23:09 -0800699 source_fp = source_info.fingerprint
700 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700701 system_updated = source_fp != target_fp
702
Tao Baoea6cbd02018-09-05 13:06:37 -0700703 source_fp_vendor = source_info.vendor_fingerprint
704 target_fp_vendor = target_info.vendor_fingerprint
705 # vendor build fingerprints could be possibly blacklisted at build time. For
706 # such a case, we consider the vendor images being changed.
707 if source_fp_vendor is None or target_fp_vendor is None:
708 vendor_updated = True
709 else:
710 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700711
712 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700713
714
Tao Bao491d7e22018-02-21 13:17:22 -0800715def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800716 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700717
Tao Bao481bab82017-12-21 11:23:09 -0800718 # We don't know what version it will be installed on top of. We expect the API
719 # just won't change very often. Similarly for fstab, it might have changed in
720 # the target build.
721 target_api_version = target_info["recovery_api_version"]
722 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700723
Tao Bao481bab82017-12-21 11:23:09 -0800724 if target_info.oem_props and not OPTIONS.oem_no_mount:
725 target_info.WriteMountOemScript(script)
726
Tao Baodf3a48b2018-01-10 16:30:43 -0800727 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700728
Tao Bao491d7e22018-02-21 13:17:22 -0800729 if not OPTIONS.no_signing:
730 staging_file = common.MakeTempFile(suffix='.zip')
731 else:
732 staging_file = output_file
733
734 output_zip = zipfile.ZipFile(
735 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
736
Doug Zongker05d3dea2009-06-22 11:32:31 -0700737 device_specific = common.DeviceSpecificParams(
738 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800739 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700740 output_zip=output_zip,
741 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700742 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700743 metadata=metadata,
744 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700745
Tao Bao457cbf62017-03-06 09:56:01 -0800746 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800747
Tao Bao481bab82017-12-21 11:23:09 -0800748 # Assertions (e.g. downgrade check, device properties check).
749 ts = target_info.GetBuildProp("ro.build.date.utc")
750 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700751 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700752
Tao Bao481bab82017-12-21 11:23:09 -0800753 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700754 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800755
756 # Two-step package strategy (in chronological order, which is *not*
757 # the order in which the generated script has things):
758 #
759 # if stage is not "2/3" or "3/3":
760 # write recovery image to boot partition
761 # set stage to "2/3"
762 # reboot to boot partition and restart recovery
763 # else if stage is "2/3":
764 # write recovery image to recovery partition
765 # set stage to "3/3"
766 # reboot to recovery partition and restart recovery
767 # else:
768 # (stage must be "3/3")
769 # set stage to ""
770 # do normal full package installation:
771 # wipe and install system, boot image, etc.
772 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700773 # complete script normally
774 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800775
776 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
777 OPTIONS.input_tmp, "RECOVERY")
778 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800779 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800780 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800781 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800782 assert fs.fs_type.upper() == "EMMC", \
783 "two-step packages only supported on devices with EMMC /misc partitions"
784 bcb_dev = {"bcb_dev": fs.device}
785 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
786 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700787if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800788""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800789
790 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
791 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800792 script.WriteRawImage("/recovery", "recovery.img")
793 script.AppendExtra("""
794set_stage("%(bcb_dev)s", "3/3");
795reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700796else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800797""" % bcb_dev)
798
Tao Baod42e97e2016-11-30 12:11:57 -0800799 # Stage 3/3: Make changes.
800 script.Comment("Stage 3/3")
801
Tao Bao6c55a8a2015-04-08 15:30:27 -0700802 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800803 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700804
Doug Zongkere5ff5902012-01-17 10:55:37 -0800805 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700806
Doug Zongker01ce19c2014-02-04 13:48:15 -0800807 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700808
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700809 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800810 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700811 if HasVendorPartition(input_zip):
812 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700813
Doug Zongker4b9596f2014-06-09 14:15:45 -0700814 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800815
Tao Baoe709b092018-02-07 12:40:00 -0800816 # See the notes in WriteBlockIncrementalOTAPackage().
817 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
818
Tao Bao457cbf62017-03-06 09:56:01 -0800819 # Full OTA is done as an "incremental" against an empty source image. This
820 # has the effect of writing new data from the package to the entire
821 # partition, but lets us reuse the updater code that writes incrementals to
822 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800823 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
824 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800825 system_tgt.ResetFileMap()
826 system_diff = common.BlockDifference("system", system_tgt, src=None)
Tao Bao76def242017-11-21 09:25:31 -0800827 system_diff.WriteScript(script, output_zip,
828 write_verify_script=OPTIONS.verify)
Doug Zongkereef39442009-04-02 12:14:19 -0700829
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700830 boot_img = common.GetBootableImage(
831 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800832
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700833 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700834 script.ShowProgress(0.1, 0)
835
Tao Baoe709b092018-02-07 12:40:00 -0800836 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
837 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800838 vendor_tgt.ResetFileMap()
839 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Tao Bao76def242017-11-21 09:25:31 -0800840 vendor_diff.WriteScript(script, output_zip,
841 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700842
Tao Bao481bab82017-12-21 11:23:09 -0800843 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700844
Tao Bao481bab82017-12-21 11:23:09 -0800845 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700846 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700847
Doug Zongker01ce19c2014-02-04 13:48:15 -0800848 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700849 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700850
Doug Zongker01ce19c2014-02-04 13:48:15 -0800851 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700852 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700853
Doug Zongker1c390a22009-05-14 19:06:36 -0700854 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700855 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700856
Doug Zongker14833602010-02-02 13:12:04 -0800857 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800858
Doug Zongker922206e2014-03-04 13:16:24 -0800859 if OPTIONS.wipe_user_data:
860 script.ShowProgress(0.1, 10)
861 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700862
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800863 if OPTIONS.two_step:
864 script.AppendExtra("""
865set_stage("%(bcb_dev)s", "");
866""" % bcb_dev)
867 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800868
869 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
870 script.Comment("Stage 1/3")
871 _WriteRecoveryImageToBoot(script, output_zip)
872
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800873 script.AppendExtra("""
874set_stage("%(bcb_dev)s", "2/3");
875reboot_now("%(bcb_dev)s", "");
876endif;
877endif;
878""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800879
Tao Bao5d182562016-02-23 11:38:39 -0800880 script.SetProgress(1)
881 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800882 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800883
884 # We haven't written the metadata entry, which will be done in
885 # FinalizeMetadata.
886 common.ZipClose(output_zip)
887
888 needed_property_files = (
889 NonAbOtaPropertyFiles(),
890 )
891 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700892
Doug Zongkerfc44a512014-08-26 13:10:25 -0700893
Doug Zongker2ea21062010-04-28 16:05:21 -0700894def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800895 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
896 common.ZipWriteStr(output_zip, METADATA_NAME, value,
897 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700898
Doug Zongkerfc44a512014-08-26 13:10:25 -0700899
Tao Bao481bab82017-12-21 11:23:09 -0800900def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800901 # Only incremental OTAs are allowed to reach here.
902 assert OPTIONS.incremental_source is not None
903
Tao Bao481bab82017-12-21 11:23:09 -0800904 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
905 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800906 is_downgrade = long(post_timestamp) < long(pre_timestamp)
907
908 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800909 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700910 raise RuntimeError(
911 "--downgrade or --override_timestamp specified but no downgrade "
912 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800913 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800914 else:
915 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700916 raise RuntimeError(
917 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
918 "Need to specify --override_timestamp OR --downgrade to allow "
919 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800920
921
Tao Baodf3a48b2018-01-10 16:30:43 -0800922def GetPackageMetadata(target_info, source_info=None):
923 """Generates and returns the metadata dict.
924
925 It generates a dict() that contains the info to be written into an OTA
926 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700927 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800928
929 Args:
930 target_info: The BuildInfo instance that holds the target build info.
931 source_info: The BuildInfo instance that holds the source build info, or
932 None if generating full OTA.
933
934 Returns:
935 A dict to be written into package metadata entry.
936 """
937 assert isinstance(target_info, BuildInfo)
938 assert source_info is None or isinstance(source_info, BuildInfo)
939
940 metadata = {
941 'post-build' : target_info.fingerprint,
942 'post-build-incremental' : target_info.GetBuildProp(
943 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800944 'post-sdk-level' : target_info.GetBuildProp(
945 'ro.build.version.sdk'),
946 'post-security-patch-level' : target_info.GetBuildProp(
947 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800948 }
949
950 if target_info.is_ab:
951 metadata['ota-type'] = 'AB'
952 metadata['ota-required-cache'] = '0'
953 else:
954 metadata['ota-type'] = 'BLOCK'
955
956 if OPTIONS.wipe_user_data:
957 metadata['ota-wipe'] = 'yes'
958
959 is_incremental = source_info is not None
960 if is_incremental:
961 metadata['pre-build'] = source_info.fingerprint
962 metadata['pre-build-incremental'] = source_info.GetBuildProp(
963 'ro.build.version.incremental')
964 metadata['pre-device'] = source_info.device
965 else:
966 metadata['pre-device'] = target_info.device
967
Tao Baofaa8e0b2018-04-12 14:31:43 -0700968 # Use the actual post-timestamp, even for a downgrade case.
969 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
970
971 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800972 if is_incremental:
973 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800974
975 return metadata
976
977
Tao Baod3fc38a2018-03-08 16:09:01 -0800978class PropertyFiles(object):
979 """A class that computes the property-files string for an OTA package.
980
981 A property-files string is a comma-separated string that contains the
982 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
983 can be fetched directly with the package URL along with the offset/size info.
984 These strings can be used for streaming A/B OTAs, or allowing an updater to
985 download package metadata entry directly, without paying the cost of
986 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800987
Tao Baocc8e2662018-03-01 19:30:00 -0800988 Computing the final property-files string requires two passes. Because doing
989 the whole package signing (with signapk.jar) will possibly reorder the ZIP
990 entries, which may in turn invalidate earlier computed ZIP entry offset/size
991 values.
992
993 This class provides functions to be called for each pass. The general flow is
994 as follows.
995
Tao Baod3fc38a2018-03-08 16:09:01 -0800996 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -0800997 # The first pass, which writes placeholders before doing initial signing.
998 property_files.Compute()
999 SignOutput()
1000
1001 # The second pass, by replacing the placeholders with actual data.
1002 property_files.Finalize()
1003 SignOutput()
1004
1005 And the caller can additionally verify the final result.
1006
1007 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001008 """
1009
Tao Baocc8e2662018-03-01 19:30:00 -08001010 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001011 self.name = None
1012 self.required = ()
1013 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001014
Tao Baocc8e2662018-03-01 19:30:00 -08001015 def Compute(self, input_zip):
1016 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001017
Tao Baocc8e2662018-03-01 19:30:00 -08001018 We reserve extra space for the offset and size of the metadata entry itself,
1019 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001020
Tao Baocc8e2662018-03-01 19:30:00 -08001021 Args:
1022 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001023
Tao Baocc8e2662018-03-01 19:30:00 -08001024 Returns:
1025 A string with placeholders for the metadata offset/size info, e.g.
1026 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1027 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001028 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001029
Tao Baod2ce2ed2018-03-16 12:59:42 -07001030 class InsufficientSpaceException(Exception):
1031 pass
1032
Tao Baocc8e2662018-03-01 19:30:00 -08001033 def Finalize(self, input_zip, reserved_length):
1034 """Finalizes a property-files string with actual METADATA offset/size info.
1035
1036 The input ZIP file has been signed, with the ZIP entries in the desired
1037 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1038 the ZIP entry offsets and construct the property-files string with actual
1039 data. Note that during this process, we must pad the property-files string
1040 to the reserved length, so that the METADATA entry size remains the same.
1041 Otherwise the entries' offsets and sizes may change again.
1042
1043 Args:
1044 input_zip: The input ZIP file.
1045 reserved_length: The reserved length of the property-files string during
1046 the call to Compute(). The final string must be no more than this
1047 size.
1048
1049 Returns:
1050 A property-files string including the metadata offset/size info, e.g.
1051 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1052
1053 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001054 InsufficientSpaceException: If the reserved length is insufficient to hold
1055 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001056 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001057 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001058 if len(result) > reserved_length:
1059 raise self.InsufficientSpaceException(
1060 'Insufficient reserved space: reserved={}, actual={}'.format(
1061 reserved_length, len(result)))
1062
Tao Baocc8e2662018-03-01 19:30:00 -08001063 result += ' ' * (reserved_length - len(result))
1064 return result
1065
1066 def Verify(self, input_zip, expected):
1067 """Verifies the input ZIP file contains the expected property-files string.
1068
1069 Args:
1070 input_zip: The input ZIP file.
1071 expected: The property-files string that's computed from Finalize().
1072
1073 Raises:
1074 AssertionError: On finding a mismatch.
1075 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001076 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001077 assert actual == expected, \
1078 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1079
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001080 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1081 """
1082 Constructs the property-files string per request.
1083
1084 Args:
1085 zip_file: The input ZIP file.
1086 reserved_length: The reserved length of the property-files string.
1087
1088 Returns:
1089 A property-files string including the metadata offset/size info, e.g.
1090 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1091 """
Tao Baocc8e2662018-03-01 19:30:00 -08001092
1093 def ComputeEntryOffsetSize(name):
1094 """Computes the zip entry offset and size."""
1095 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001096 offset = info.header_offset
1097 offset += zipfile.sizeFileHeader
1098 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001099 size = info.file_size
1100 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1101
1102 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001103 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001104 for entry in self.required:
1105 tokens.append(ComputeEntryOffsetSize(entry))
1106 for entry in self.optional:
1107 if entry in zip_file.namelist():
1108 tokens.append(ComputeEntryOffsetSize(entry))
1109
1110 # 'META-INF/com/android/metadata' is required. We don't know its actual
1111 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001112 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1113 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1114 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1115 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001116 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001117 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001118 else:
1119 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1120
1121 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001122
Tao Bao85f16982018-03-08 16:28:33 -08001123 def _GetPrecomputed(self, input_zip):
1124 """Computes the additional tokens to be included into the property-files.
1125
1126 This applies to tokens without actual ZIP entries, such as
1127 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1128 that they can download the payload metadata directly with the info.
1129
1130 Args:
1131 input_zip: The input zip file.
1132
1133 Returns:
1134 A list of strings (tokens) to be added to the property-files string.
1135 """
1136 # pylint: disable=no-self-use
1137 # pylint: disable=unused-argument
1138 return []
1139
Tao Baofe5b69a2018-03-02 09:47:43 -08001140
Tao Baod3fc38a2018-03-08 16:09:01 -08001141class StreamingPropertyFiles(PropertyFiles):
1142 """A subclass for computing the property-files for streaming A/B OTAs."""
1143
1144 def __init__(self):
1145 super(StreamingPropertyFiles, self).__init__()
1146 self.name = 'ota-streaming-property-files'
1147 self.required = (
1148 # payload.bin and payload_properties.txt must exist.
1149 'payload.bin',
1150 'payload_properties.txt',
1151 )
1152 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001153 # care_map is available only if dm-verity is enabled.
1154 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001155 'care_map.txt',
1156 # compatibility.zip is available only if target supports Treble.
1157 'compatibility.zip',
1158 )
1159
1160
Tao Bao85f16982018-03-08 16:28:33 -08001161class AbOtaPropertyFiles(StreamingPropertyFiles):
1162 """The property-files for A/B OTA that includes payload_metadata.bin info.
1163
1164 Since P, we expose one more token (aka property-file), in addition to the ones
1165 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1166 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1167 doesn't exist as a separate ZIP entry, but can be used to verify if the
1168 payload can be applied on the given device.
1169
1170 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1171 and the newly added 'ota-property-files' in P. The new token will only be
1172 available in 'ota-property-files'.
1173 """
1174
1175 def __init__(self):
1176 super(AbOtaPropertyFiles, self).__init__()
1177 self.name = 'ota-property-files'
1178
1179 def _GetPrecomputed(self, input_zip):
1180 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1181 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1182
1183 @staticmethod
1184 def _GetPayloadMetadataOffsetAndSize(input_zip):
1185 """Computes the offset and size of the payload metadata for a given package.
1186
1187 (From system/update_engine/update_metadata.proto)
1188 A delta update file contains all the deltas needed to update a system from
1189 one specific version to another specific version. The update format is
1190 represented by this struct pseudocode:
1191
1192 struct delta_update_file {
1193 char magic[4] = "CrAU";
1194 uint64 file_format_version;
1195 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1196
1197 // Only present if format_version > 1:
1198 uint32 metadata_signature_size;
1199
1200 // The Bzip2 compressed DeltaArchiveManifest
1201 char manifest[metadata_signature_size];
1202
1203 // The signature of the metadata (from the beginning of the payload up to
1204 // this location, not including the signature itself). This is a
1205 // serialized Signatures message.
1206 char medatada_signature_message[metadata_signature_size];
1207
1208 // Data blobs for files, no specific format. The specific offset
1209 // and length of each data blob is recorded in the DeltaArchiveManifest.
1210 struct {
1211 char data[];
1212 } blobs[];
1213
1214 // These two are not signed:
1215 uint64 payload_signatures_message_size;
1216 char payload_signatures_message[];
1217 };
1218
1219 'payload-metadata.bin' contains all the bytes from the beginning of the
1220 payload, till the end of 'medatada_signature_message'.
1221 """
1222 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001223 payload_offset = payload_info.header_offset
1224 payload_offset += zipfile.sizeFileHeader
1225 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001226 payload_size = payload_info.file_size
1227
1228 with input_zip.open('payload.bin', 'r') as payload_fp:
1229 header_bin = payload_fp.read(24)
1230
1231 # network byte order (big-endian)
1232 header = struct.unpack("!IQQL", header_bin)
1233
1234 # 'CrAU'
1235 magic = header[0]
1236 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1237
1238 manifest_size = header[2]
1239 metadata_signature_size = header[3]
1240 metadata_total = 24 + manifest_size + metadata_signature_size
1241 assert metadata_total < payload_size
1242
1243 return (payload_offset, metadata_total)
1244
1245
Tao Bao491d7e22018-02-21 13:17:22 -08001246class NonAbOtaPropertyFiles(PropertyFiles):
1247 """The property-files for non-A/B OTA.
1248
1249 For non-A/B OTA, the property-files string contains the info for METADATA
1250 entry, with which a system updater can be fetched the package metadata prior
1251 to downloading the entire package.
1252 """
1253
1254 def __init__(self):
1255 super(NonAbOtaPropertyFiles, self).__init__()
1256 self.name = 'ota-property-files'
1257
1258
Tao Baod3fc38a2018-03-08 16:09:01 -08001259def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001260 """Finalizes the metadata and signs an A/B OTA package.
1261
1262 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1263 that contains the offsets and sizes for the ZIP entries. An example
1264 property-files string is as follows.
1265
1266 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1267
1268 OTA server can pass down this string, in addition to the package URL, to the
1269 system update client. System update client can then fetch individual ZIP
1270 entries (ZIP_STORED) directly at the given offset of the URL.
1271
1272 Args:
1273 metadata: The metadata dict for the package.
1274 input_file: The input ZIP filename that doesn't contain the package METADATA
1275 entry yet.
1276 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001277 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001278 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001279
Tao Baod2ce2ed2018-03-16 12:59:42 -07001280 def ComputeAllPropertyFiles(input_file, needed_property_files):
1281 # Write the current metadata entry with placeholders.
1282 with zipfile.ZipFile(input_file) as input_zip:
1283 for property_files in needed_property_files:
1284 metadata[property_files.name] = property_files.Compute(input_zip)
1285 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001286
Tao Baod2ce2ed2018-03-16 12:59:42 -07001287 if METADATA_NAME in namelist:
1288 common.ZipDelete(input_file, METADATA_NAME)
1289 output_zip = zipfile.ZipFile(input_file, 'a')
1290 WriteMetadata(metadata, output_zip)
1291 common.ZipClose(output_zip)
1292
1293 if OPTIONS.no_signing:
1294 return input_file
1295
Tao Bao491d7e22018-02-21 13:17:22 -08001296 prelim_signing = common.MakeTempFile(suffix='.zip')
1297 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001298 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001299
Tao Baod2ce2ed2018-03-16 12:59:42 -07001300 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1301 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1302 for property_files in needed_property_files:
1303 metadata[property_files.name] = property_files.Finalize(
1304 prelim_signing_zip, len(metadata[property_files.name]))
1305
1306 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1307 # entries, as well as padding the entry headers. We do a preliminary signing
1308 # (with an incomplete metadata entry) to allow that to happen. Then compute
1309 # the ZIP entry offsets, write back the final metadata and do the final
1310 # signing.
1311 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1312 try:
1313 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1314 except PropertyFiles.InsufficientSpaceException:
1315 # Even with the preliminary signing, the entry orders may change
1316 # dramatically, which leads to insufficiently reserved space during the
1317 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1318 # preliminary signing works, based on the already ordered ZIP entries, to
1319 # address the issue.
1320 prelim_signing = ComputeAllPropertyFiles(
1321 prelim_signing, needed_property_files)
1322 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001323
1324 # Replace the METADATA entry.
1325 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001326 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001327 WriteMetadata(metadata, output_zip)
1328 common.ZipClose(output_zip)
1329
1330 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001331 if OPTIONS.no_signing:
1332 output_file = prelim_signing
1333 else:
1334 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001335
1336 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001337 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001338 for property_files in needed_property_files:
1339 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001340
1341
Tao Bao491d7e22018-02-21 13:17:22 -08001342def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001343 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1344 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001345
Tao Bao481bab82017-12-21 11:23:09 -08001346 target_api_version = target_info["recovery_api_version"]
1347 source_api_version = source_info["recovery_api_version"]
1348 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001349 logger.warning(
1350 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001351
Tao Bao481bab82017-12-21 11:23:09 -08001352 script = edify_generator.EdifyGenerator(
1353 source_api_version, target_info, fstab=source_info["fstab"])
1354
1355 if target_info.oem_props or source_info.oem_props:
1356 if not OPTIONS.oem_no_mount:
1357 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001358
Tao Baodf3a48b2018-01-10 16:30:43 -08001359 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001360
Tao Bao491d7e22018-02-21 13:17:22 -08001361 if not OPTIONS.no_signing:
1362 staging_file = common.MakeTempFile(suffix='.zip')
1363 else:
1364 staging_file = output_file
1365
1366 output_zip = zipfile.ZipFile(
1367 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1368
Geremy Condra36bd3652014-02-06 19:45:10 -08001369 device_specific = common.DeviceSpecificParams(
1370 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001371 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001372 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001373 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001374 output_zip=output_zip,
1375 script=script,
1376 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001377 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001378
Geremy Condra36bd3652014-02-06 19:45:10 -08001379 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001380 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001381 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001382 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001383 updating_boot = (not OPTIONS.two_step and
1384 (source_boot.data != target_boot.data))
1385
Geremy Condra36bd3652014-02-06 19:45:10 -08001386 target_recovery = common.GetBootableImage(
1387 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001388
Tao Baoe709b092018-02-07 12:40:00 -08001389 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1390 # shared blocks (i.e. some blocks will show up in multiple files' block
1391 # list). We can only allocate such shared blocks to the first "owner", and
1392 # disable imgdiff for all later occurrences.
1393 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1394 target_info.get('ext4_share_dup_blocks') == "true")
1395 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1396 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001397
1398 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1399 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001400 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001401 allow_shared_blocks,
1402 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001403
Tao Bao0582cb62017-12-21 11:47:01 -08001404 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001405 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001406 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001407
Tao Baof8acad12016-07-07 09:09:58 -07001408 # Check the first block of the source system partition for remount R/W only
1409 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001410 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001411 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001412 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1413 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1414 # b) the blocks listed in block map may not contain all the bytes for a given
1415 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001416 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001417 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1418 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001419 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001420 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001421 version=blockimgdiff_version,
1422 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001423
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001424 if HasVendorPartition(target_zip):
1425 if not HasVendorPartition(source_zip):
1426 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001427 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1428 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001429 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1430 "vendor", 4096, target_info)
1431 vendor_tgt = common.GetSparseImage(
1432 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1433 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001434
1435 # Check first block of vendor partition for remount R/W only if
1436 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001437 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001438 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001439 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001440 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001441 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001442 version=blockimgdiff_version,
1443 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001444 else:
1445 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001446
Tao Baobcd1d162017-08-26 13:10:26 -07001447 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001448 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001449
Tao Bao481bab82017-12-21 11:23:09 -08001450 # Assertions (e.g. device properties check).
1451 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001452 device_specific.IncrementalOTA_Assertions()
1453
1454 # Two-step incremental package strategy (in chronological order,
1455 # which is *not* the order in which the generated script has
1456 # things):
1457 #
1458 # if stage is not "2/3" or "3/3":
1459 # do verification on current system
1460 # write recovery image to boot partition
1461 # set stage to "2/3"
1462 # reboot to boot partition and restart recovery
1463 # else if stage is "2/3":
1464 # write recovery image to recovery partition
1465 # set stage to "3/3"
1466 # reboot to recovery partition and restart recovery
1467 # else:
1468 # (stage must be "3/3")
1469 # perform update:
1470 # patch system files, etc.
1471 # force full install of new boot image
1472 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001473 # complete script normally
1474 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001475
1476 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001477 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001478 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001479 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001480 assert fs.fs_type.upper() == "EMMC", \
1481 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001482 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001483 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1484 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001485if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001486""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001487
1488 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1489 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001490 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001491 script.WriteRawImage("/recovery", "recovery.img")
1492 script.AppendExtra("""
1493set_stage("%(bcb_dev)s", "3/3");
1494reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001495else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001496""" % bcb_dev)
1497
Tao Baod42e97e2016-11-30 12:11:57 -08001498 # Stage 1/3: (a) Verify the current system.
1499 script.Comment("Stage 1/3")
1500
Tao Bao6c55a8a2015-04-08 15:30:27 -07001501 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001502 script.Print("Source: {}".format(source_info.fingerprint))
1503 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001504
Geremy Condra36bd3652014-02-06 19:45:10 -08001505 script.Print("Verifying current system...")
1506
1507 device_specific.IncrementalOTA_VerifyBegin()
1508
Tao Bao481bab82017-12-21 11:23:09 -08001509 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001510
Tao Baod8d14be2016-02-04 14:26:02 -08001511 # Check the required cache size (i.e. stashed blocks).
1512 size = []
1513 if system_diff:
1514 size.append(system_diff.required_cache)
1515 if vendor_diff:
1516 size.append(vendor_diff.required_cache)
1517
Geremy Condra36bd3652014-02-06 19:45:10 -08001518 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001519 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001520 d = common.Difference(target_boot, source_boot)
1521 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001522 if d is None:
1523 include_full_boot = True
1524 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1525 else:
1526 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001527
Tao Bao32fcdab2018-10-12 10:30:39 -07001528 logger.info(
1529 "boot target: %d source: %d diff: %d", target_boot.size,
1530 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001531
Tao Bao51216552018-08-26 11:53:15 -07001532 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001533
Tao Bao51216552018-08-26 11:53:15 -07001534 script.PatchPartitionCheck(
1535 "{}:{}:{}:{}".format(
1536 boot_type, boot_device, target_boot.size, target_boot.sha1),
1537 "{}:{}:{}:{}".format(
1538 boot_type, boot_device, source_boot.size, source_boot.sha1))
1539
Tao Baod8d14be2016-02-04 14:26:02 -08001540 size.append(target_boot.size)
1541
1542 if size:
1543 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001544
1545 device_specific.IncrementalOTA_VerifyEnd()
1546
1547 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001548 # Stage 1/3: (b) Write recovery image to /boot.
1549 _WriteRecoveryImageToBoot(script, output_zip)
1550
Geremy Condra36bd3652014-02-06 19:45:10 -08001551 script.AppendExtra("""
1552set_stage("%(bcb_dev)s", "2/3");
1553reboot_now("%(bcb_dev)s", "");
1554else
1555""" % bcb_dev)
1556
Tao Baod42e97e2016-11-30 12:11:57 -08001557 # Stage 3/3: Make changes.
1558 script.Comment("Stage 3/3")
1559
Jesse Zhao75bcea02015-01-06 10:59:53 -08001560 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001561 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001562 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001563 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001564
Geremy Condra36bd3652014-02-06 19:45:10 -08001565 script.Comment("---- start making changes here ----")
1566
1567 device_specific.IncrementalOTA_InstallBegin()
1568
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001569 system_diff.WriteScript(script, output_zip,
Tao Bao76def242017-11-21 09:25:31 -08001570 progress=0.8 if vendor_diff else 0.9,
1571 write_verify_script=OPTIONS.verify)
Tao Bao68658c02015-06-01 13:40:49 -07001572
Doug Zongkerfc44a512014-08-26 13:10:25 -07001573 if vendor_diff:
Tao Bao76def242017-11-21 09:25:31 -08001574 vendor_diff.WriteScript(script, output_zip, progress=0.1,
1575 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001576
1577 if OPTIONS.two_step:
1578 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1579 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001580 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001581
1582 if not OPTIONS.two_step:
1583 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001584 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001585 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001586 script.Print("Installing boot image...")
1587 script.WriteRawImage("/boot", "boot.img")
1588 else:
1589 # Produce the boot image by applying a patch to the current
1590 # contents of the boot partition, and write it back to the
1591 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001592 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001593 script.Print("Patching boot image...")
1594 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001595 script.PatchPartition(
1596 '{}:{}:{}:{}'.format(
1597 boot_type, boot_device, target_boot.size, target_boot.sha1),
1598 '{}:{}:{}:{}'.format(
1599 boot_type, boot_device, source_boot.size, source_boot.sha1),
1600 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001601 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001602 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001603
1604 # Do device-specific installation (eg, write radio image).
1605 device_specific.IncrementalOTA_InstallEnd()
1606
1607 if OPTIONS.extra_script is not None:
1608 script.AppendExtra(OPTIONS.extra_script)
1609
Doug Zongker922206e2014-03-04 13:16:24 -08001610 if OPTIONS.wipe_user_data:
1611 script.Print("Erasing user data...")
1612 script.FormatPartition("/data")
1613
Geremy Condra36bd3652014-02-06 19:45:10 -08001614 if OPTIONS.two_step:
1615 script.AppendExtra("""
1616set_stage("%(bcb_dev)s", "");
1617endif;
1618endif;
1619""" % bcb_dev)
1620
1621 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001622 # For downgrade OTAs, we prefer to use the update-binary in the source
1623 # build that is actually newer than the one in the target build.
1624 if OPTIONS.downgrade:
1625 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1626 else:
1627 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001628 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001629
1630 # We haven't written the metadata entry yet, which will be handled in
1631 # FinalizeMetadata().
1632 common.ZipClose(output_zip)
1633
1634 # Sign the generated zip package unless no_signing is specified.
1635 needed_property_files = (
1636 NonAbOtaPropertyFiles(),
1637 )
1638 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001639
Doug Zongker32b527d2014-03-04 10:03:02 -08001640
Tao Bao15a146a2018-02-21 16:06:59 -08001641def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001642 """Returns a target-files.zip file for generating secondary payload.
1643
1644 Although the original target-files.zip already contains secondary slot
1645 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1646 ones without _other suffix. Note that we cannot instead modify the names in
1647 META/ab_partitions.txt, because there are no matching partitions on device.
1648
1649 For the partitions that don't have secondary images, the ones for primary
1650 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1651 bootloader images in the inactive slot.
1652
1653 Args:
1654 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001655 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001656
1657 Returns:
1658 The filename of the target-files.zip for generating secondary payload.
1659 """
1660 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1661 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1662
Tao Baodba59ee2018-01-09 13:21:02 -08001663 with zipfile.ZipFile(input_file, 'r') as input_zip:
1664 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001665 namelist = input_zip.namelist()
1666
1667 # Additionally unzip 'RADIO/*' if exists.
1668 unzip_pattern = UNZIP_PATTERN[:]
1669 if any([entry.startswith('RADIO/') for entry in namelist]):
1670 unzip_pattern.append('RADIO/*')
1671 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001672
1673 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001674 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1675 if info.filename == 'IMAGES/system_other.img':
1676 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1677
1678 # Primary images and friends need to be skipped explicitly.
1679 elif info.filename in ('IMAGES/system.img',
1680 'IMAGES/system.map'):
1681 pass
1682
Tao Bao15a146a2018-02-21 16:06:59 -08001683 # Skip copying the postinstall config if requested.
1684 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1685 pass
1686
Tao Bao12489802018-07-12 14:47:38 -07001687 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001688 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1689
Tao Baof7140c02018-01-30 17:09:24 -08001690 common.ZipClose(target_zip)
1691
1692 return target_file
1693
1694
Tao Bao15a146a2018-02-21 16:06:59 -08001695def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1696 """Returns a target-files.zip that's not containing postinstall_config.txt.
1697
1698 This allows brillo_update_payload script to skip writing all the postinstall
1699 hooks in the generated payload. The input target-files.zip file will be
1700 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1701 contain the postinstall_config.txt entry, the input file will be returned.
1702
1703 Args:
1704 input_file: The input target-files.zip filename.
1705
1706 Returns:
1707 The filename of target-files.zip that doesn't contain postinstall config.
1708 """
1709 # We should only make a copy if postinstall_config entry exists.
1710 with zipfile.ZipFile(input_file, 'r') as input_zip:
1711 if POSTINSTALL_CONFIG not in input_zip.namelist():
1712 return input_file
1713
1714 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1715 shutil.copyfile(input_file, target_file)
1716 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1717 return target_file
1718
1719
Tao Baoc098e9e2016-01-07 13:03:56 -08001720def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1721 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001722 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001723 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001724 if not OPTIONS.no_signing:
1725 staging_file = common.MakeTempFile(suffix='.zip')
1726 else:
1727 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001728 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001729 compression=zipfile.ZIP_DEFLATED)
1730
Tao Bao481bab82017-12-21 11:23:09 -08001731 if source_file is not None:
1732 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1733 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1734 else:
1735 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1736 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001737
Tao Bao481bab82017-12-21 11:23:09 -08001738 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001739 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001740
Tao Bao15a146a2018-02-21 16:06:59 -08001741 if OPTIONS.skip_postinstall:
1742 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1743
Tao Bao40b18822018-01-30 18:19:04 -08001744 # Generate payload.
1745 payload = Payload()
1746
1747 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001748 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001749 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001750 else:
1751 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001752 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001753
Tao Bao40b18822018-01-30 18:19:04 -08001754 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001755
Tao Bao40b18822018-01-30 18:19:04 -08001756 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001757 payload_signer = PayloadSigner()
1758 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001759
Tao Bao40b18822018-01-30 18:19:04 -08001760 # Write the payload into output zip.
1761 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001762
Tao Baof7140c02018-01-30 17:09:24 -08001763 # Generate and include the secondary payload that installs secondary images
1764 # (e.g. system_other.img).
1765 if OPTIONS.include_secondary:
1766 # We always include a full payload for the secondary slot, even when
1767 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001768 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1769 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001770 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001771 secondary_payload.Generate(secondary_target_file,
1772 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001773 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001774 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001775
Tianjie Xucfa86222016-03-07 16:31:19 -08001776 # If dm-verity is supported for the device, copy contents of care_map
1777 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001778 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001779 if (target_info.get("verity") == "true" or
1780 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001781 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1782 "META/" + x in target_zip.namelist()]
1783
1784 # Adds care_map if either the protobuf format or the plain text one exists.
1785 if care_map_list:
1786 care_map_name = care_map_list[0]
1787 care_map_data = target_zip.read("META/" + care_map_name)
1788 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001789 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001790 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001791 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001792 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001793 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001794
Tao Baobcd1d162017-08-26 13:10:26 -07001795 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001796 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001797
Tao Bao21803d32017-04-19 10:16:09 -07001798 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001799
Tao Baofe5b69a2018-03-02 09:47:43 -08001800 # We haven't written the metadata entry yet, which will be handled in
1801 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001802 common.ZipClose(output_zip)
1803
Tao Bao85f16982018-03-08 16:28:33 -08001804 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1805 # all the info of the latter. However, system updaters and OTA servers need to
1806 # take time to switch to the new flag. We keep both of the flags for
1807 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001808 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001809 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001810 StreamingPropertyFiles(),
1811 )
1812 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001813
Tao Baoc098e9e2016-01-07 13:03:56 -08001814
Doug Zongkereef39442009-04-02 12:14:19 -07001815def main(argv):
1816
1817 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001818 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001819 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001820 elif o in ("-i", "--incremental_from"):
1821 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001822 elif o == "--full_radio":
1823 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001824 elif o == "--full_bootloader":
1825 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001826 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001827 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001828 elif o == "--downgrade":
1829 OPTIONS.downgrade = True
1830 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001831 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001832 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001833 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001834 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001835 elif o == "--oem_no_mount":
1836 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001837 elif o in ("-e", "--extra_script"):
1838 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001839 elif o in ("-t", "--worker_threads"):
1840 if a.isdigit():
1841 OPTIONS.worker_threads = int(a)
1842 else:
1843 raise ValueError("Cannot parse value %r for option %r - only "
1844 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001845 elif o in ("-2", "--two_step"):
1846 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001847 elif o == "--include_secondary":
1848 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001849 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001850 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001851 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001852 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001853 elif o == "--block":
1854 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001855 elif o in ("-b", "--binary"):
1856 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001857 elif o == "--stash_threshold":
1858 try:
1859 OPTIONS.stash_threshold = float(a)
1860 except ValueError:
1861 raise ValueError("Cannot parse value %r for option %r - expecting "
1862 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001863 elif o == "--log_diff":
1864 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001865 elif o == "--payload_signer":
1866 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001867 elif o == "--payload_signer_args":
1868 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001869 elif o == "--extracted_input_target_files":
1870 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001871 elif o == "--skip_postinstall":
1872 OPTIONS.skip_postinstall = True
Doug Zongkereef39442009-04-02 12:14:19 -07001873 else:
1874 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001875 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001876
1877 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001878 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001879 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001880 "package_key=",
1881 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001882 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001883 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001884 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001885 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001886 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001887 "extra_script=",
1888 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001889 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001890 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001891 "no_signing",
1892 "block",
1893 "binary=",
1894 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001895 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001896 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001897 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001898 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001899 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001900 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001901 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001902 "skip_postinstall",
Dan Albert8b72aef2015-03-23 19:13:21 -07001903 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001904
1905 if len(args) != 2:
1906 common.Usage(__doc__)
1907 sys.exit(1)
1908
Tao Bao32fcdab2018-10-12 10:30:39 -07001909 common.InitLogging()
1910
Tao Bao5d182562016-02-23 11:38:39 -08001911 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08001912 # We should only allow downgrading incrementals (as opposed to full).
1913 # Otherwise the device may go back from arbitrary build with this full
1914 # OTA package.
1915 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001916 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001917
Tao Bao2db13852018-01-08 22:28:57 -08001918 # Load the build info dicts from the zip directly or the extracted input
1919 # directory. We don't need to unzip the entire target-files zips, because they
1920 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1921 # When loading the info dicts, we don't need to provide the second parameter
1922 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1923 # some properties with their actual paths, such as 'selinux_fc',
1924 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001925 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001926 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001927 else:
Tao Bao2db13852018-01-08 22:28:57 -08001928 with zipfile.ZipFile(args[0], 'r') as input_zip:
1929 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001930
Tao Bao32fcdab2018-10-12 10:30:39 -07001931 logger.info("--- target info ---")
1932 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08001933
1934 # Load the source build dict if applicable.
1935 if OPTIONS.incremental_source is not None:
1936 OPTIONS.target_info_dict = OPTIONS.info_dict
1937 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1938 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1939
Tao Bao32fcdab2018-10-12 10:30:39 -07001940 logger.info("--- source info ---")
1941 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08001942
1943 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001944 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1945
Tao Baoc098e9e2016-01-07 13:03:56 -08001946 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1947
Christian Oderf63e2cd2017-05-01 22:30:15 +02001948 # Use the default key to sign the package if not specified with package_key.
1949 # package_keys are needed on ab_updates, so always define them if an
1950 # ab_update is getting created.
1951 if not OPTIONS.no_signing or ab_update:
1952 if OPTIONS.package_key is None:
1953 OPTIONS.package_key = OPTIONS.info_dict.get(
1954 "default_system_dev_certificate",
1955 "build/target/product/security/testkey")
1956 # Get signing keys
1957 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1958
Tao Baoc098e9e2016-01-07 13:03:56 -08001959 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001960 WriteABOTAPackageWithBrilloScript(
1961 target_file=args[0],
1962 output_file=args[1],
1963 source_file=OPTIONS.incremental_source)
1964
Tao Bao32fcdab2018-10-12 10:30:39 -07001965 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001966 return
1967
Tao Bao2db13852018-01-08 22:28:57 -08001968 # Sanity check the loaded info dicts first.
1969 if OPTIONS.info_dict.get("no_recovery") == "true":
1970 raise common.ExternalError(
1971 "--- target build has specified no recovery ---")
1972
1973 # Non-A/B OTAs rely on /cache partition to store temporary files.
1974 cache_size = OPTIONS.info_dict.get("cache_size")
1975 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07001976 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08001977 OPTIONS.cache_size = cache_size
1978
Doug Zongker1c390a22009-05-14 19:06:36 -07001979 if OPTIONS.extra_script is not None:
1980 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1981
Dan Willemsencea5cd22017-03-21 14:44:27 -07001982 if OPTIONS.extracted_input is not None:
1983 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001984 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001985 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08001986 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001987 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001988
Tao Bao2db13852018-01-08 22:28:57 -08001989 # If the caller explicitly specified the device-specific extensions path via
1990 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1991 # is present in the target target_files. Otherwise, take the path of the file
1992 # from 'tool_extensions' in the info dict and look for that in the local
1993 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001994 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001995 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1996 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07001997 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001998 OPTIONS.device_specific = from_input
1999 else:
Tao Bao2db13852018-01-08 22:28:57 -08002000 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002001
Doug Zongker37974732010-09-16 17:44:38 -07002002 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002003 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002004
Tao Bao767e3ac2015-11-10 12:19:19 -08002005 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002006 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002007 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002008 WriteFullOTAPackage(
2009 input_zip,
2010 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002011
Tao Bao32b80dc2018-01-08 22:50:47 -08002012 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002013 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002014 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002015 OPTIONS.source_tmp = common.UnzipTemp(
2016 OPTIONS.incremental_source, UNZIP_PATTERN)
2017 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2018 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002019 WriteBlockIncrementalOTAPackage(
2020 input_zip,
2021 source_zip,
2022 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002023
2024 if OPTIONS.log_diff:
2025 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002026 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002027 target_files_diff.recursiveDiff(
2028 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002029
Tao Bao32fcdab2018-10-12 10:30:39 -07002030 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002031
2032
2033if __name__ == '__main__':
2034 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002035 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002036 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002037 except common.ExternalError:
2038 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002039 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002040 finally:
2041 common.Cleanup()