blob: e21f44e11d2bc01b327ec1dba69205b1a8d3fb43 [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"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongkerafb32ea2011-09-22 10:28:04 -070024 -k (--package_key) <key> Key to use to sign the package (default is
25 the value of default_system_dev_certificate from the input
26 target-files's META/misc_info.txt, or
27 "build/target/product/security/testkey" if that value is not
28 specified).
29
30 For incremental OTAs, the default value is based on the source
31 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070032
33 -i (--incremental_from) <file>
34 Generate an incremental OTA using the given target-files zip as
35 the starting build.
36
Tao Bao43078aa2015-04-21 14:32:35 -070037 --full_radio
38 When generating an incremental OTA, always include a full copy of
39 radio image. This option is only meaningful when -i is specified,
40 because a full radio is always included in a full OTA if applicable.
41
leozwangaa6c1a12015-08-14 10:57:58 -070042 --full_bootloader
43 Similar to --full_radio. When generating an incremental OTA, always
44 include a full copy of bootloader image.
45
Tao Baoedb35b82017-10-30 16:07:13 -070046 --verify
47 Remount and verify the checksums of the files written to the system and
48 vendor (if used) partitions. Non-A/B incremental OTAs only.
Michael Runge63f01de2014-10-28 19:24:19 -070049
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080050 -o (--oem_settings) <main_file[,additional_files...]>
51 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -080052 properties on the OEM partition of the intended device. Multiple expected
53 values can be used by providing multiple files. Only the first dict will
54 be used to compute fingerprint, while the rest will be used to assert
55 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080056
Tao Bao8608cde2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Tao Bao337633f2017-12-06 15:20:19 -080063 --wipe_user_data
Doug Zongkerdbfaae52009-04-21 17:12:54 -070064 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Tao Bao5d182562016-02-23 11:38:39 -080067 --downgrade
68 Intentionally generate an incremental OTA that updates from a newer
69 build to an older one (based on timestamp comparison). "post-timestamp"
70 will be replaced by "ota-downgrade=yes" in the metadata file. A data
71 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080072 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080073 the OTA package, unless --binary flag is specified. Please also check the
74 doc for --override_timestamp below.
75
76 --override_timestamp
77 Intentionally generate an incremental OTA that updates from a newer
78 build to an older one (based on timestamp comparison), by overriding the
79 timestamp in package metadata. This differs from --downgrade flag: we
80 know for sure this is NOT an actual downgrade case, but two builds are
81 cut in a reverse order. A legit use case is that we cut a new build C
82 (after having A and B), but want to enfore an update path of A -> C -> B.
83 Specifying --downgrade may not help since that would enforce a data wipe
84 for C -> B update. The value of "post-timestamp" will be set to the newer
85 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080086
Doug Zongker1c390a22009-05-14 19:06:36 -070087 -e (--extra_script) <file>
88 Insert the contents of file at the end of the update script.
89
Doug Zongker9b23f2c2013-11-25 14:44:12 -080090 -2 (--two_step)
91 Generate a 'two-step' OTA package, where recovery is updated
92 first, so that any changes made to the system partition are done
93 using the new recovery (new kernel, etc.).
94
Doug Zongker26e66192014-02-20 13:22:07 -080095 --block
Tao Bao457cbf62017-03-06 09:56:01 -080096 Generate a block-based OTA for non-A/B device. We have deprecated the
97 support for file-based OTA since O. Block-based OTA will be used by
98 default for all non-A/B devices. Keeping this flag here to not break
99 existing callers.
Doug Zongker26e66192014-02-20 13:22:07 -0800100
Doug Zongker25568482014-03-03 10:21:27 -0800101 -b (--binary) <file>
102 Use the given binary as the update-binary in the output package,
103 instead of the binary in the build's target_files. Use for
104 development only.
105
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200106 -t (--worker_threads) <int>
107 Specifies the number of worker-threads that will be used when
108 generating patches for incremental updates (defaults to 3).
109
Tao Bao8dcf7382015-05-21 14:09:49 -0700110 --stash_threshold <float>
111 Specifies the threshold that will be used to compute the maximum
112 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800113
Tao Baod62c6032015-11-30 09:40:20 -0800114 --log_diff <file>
115 Generate a log file that shows the differences in the source and target
116 builds for an incremental package. This option is only meaningful when
117 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700118
119 --payload_signer <signer>
120 Specify the signer when signing the payload and metadata for A/B OTAs.
121 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
122 with the package private key. If the private key cannot be accessed
123 directly, a payload signer that knows how to do that should be specified.
124 The signer will be supplied with "-inkey <path_to_key>",
125 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700126
127 --payload_signer_args <args>
128 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700129"""
130
Tao Bao89fbb0f2017-01-10 10:47:58 -0800131from __future__ import print_function
132
Doug Zongkerfc44a512014-08-26 13:10:25 -0700133import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800134import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700135import shlex
Tao Bao481bab82017-12-21 11:23:09 -0800136import subprocess
137import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700138import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700139import zipfile
140
141import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700142import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700143import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700144
Tao Bao481bab82017-12-21 11:23:09 -0800145if sys.hexversion < 0x02070000:
146 print("Python 2.7 or newer is required.", file=sys.stderr)
147 sys.exit(1)
148
149
Doug Zongkereef39442009-04-02 12:14:19 -0700150OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700151OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700152OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700153OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700154OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700155OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800156OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800157OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700158OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700159OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
160if OPTIONS.worker_threads == 0:
161 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800162OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900163OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800164OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800165OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700166OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800167OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700168OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700169OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700170# Stash size cannot exceed cache_size * threshold.
171OPTIONS.cache_size = None
172OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800173OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700174OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700175OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700176OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200177OPTIONS.key_passwords = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700178
Tao Bao2dd1c482017-02-03 16:49:39 -0800179METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800180UNZIP_PATTERN = ['IMAGES/*', 'META/*']
181
Tao Bao2dd1c482017-02-03 16:49:39 -0800182
Tao Bao481bab82017-12-21 11:23:09 -0800183class BuildInfo(object):
184 """A class that holds the information for a given build.
185
186 This class wraps up the property querying for a given source or target build.
187 It abstracts away the logic of handling OEM-specific properties, and caches
188 the commonly used properties such as fingerprint.
189
190 There are two types of info dicts: a) build-time info dict, which is generated
191 at build time (i.e. included in a target_files zip); b) OEM info dict that is
192 specified at package generation time (via command line argument
193 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
194 having "oem_fingerprint_properties" in build-time info dict), all the queries
195 would be answered based on build-time info dict only. Otherwise if using
196 OEM-specific properties, some of them will be calculated from two info dicts.
197
198 Users can query properties similarly as using a dict() (e.g. info['fstab']),
199 or to query build properties via GetBuildProp() or GetVendorBuildProp().
200
201 Attributes:
202 info_dict: The build-time info dict.
203 is_ab: Whether it's a build that uses A/B OTA.
204 oem_dicts: A list of OEM dicts.
205 oem_props: A list of OEM properties that should be read from OEM dicts; None
206 if the build doesn't use any OEM-specific property.
207 fingerprint: The fingerprint of the build, which would be calculated based
208 on OEM properties if applicable.
209 device: The device name, which could come from OEM dicts if applicable.
210 """
211
212 def __init__(self, info_dict, oem_dicts):
213 """Initializes a BuildInfo instance with the given dicts.
214
215 Arguments:
216 info_dict: The build-time info dict.
217 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
218 that it always uses the first dict to calculate the fingerprint or the
219 device name. The rest would be used for asserting OEM properties only
220 (e.g. one package can be installed on one of these devices).
221 """
222 self.info_dict = info_dict
223 self.oem_dicts = oem_dicts
224
225 self._is_ab = info_dict.get("ab_update") == "true"
226 self._oem_props = info_dict.get("oem_fingerprint_properties")
227
228 if self._oem_props:
229 assert oem_dicts, "OEM source required for this build"
230
231 # These two should be computed only after setting self._oem_props.
232 self._device = self.GetOemProperty("ro.product.device")
233 self._fingerprint = self.CalculateFingerprint()
234
235 @property
236 def is_ab(self):
237 return self._is_ab
238
239 @property
240 def device(self):
241 return self._device
242
243 @property
244 def fingerprint(self):
245 return self._fingerprint
246
247 @property
248 def oem_props(self):
249 return self._oem_props
250
251 def __getitem__(self, key):
252 return self.info_dict[key]
253
254 def get(self, key, default=None):
255 return self.info_dict.get(key, default)
256
257 def GetBuildProp(self, prop):
258 """Returns the inquired build property."""
259 try:
260 return self.info_dict.get("build.prop", {})[prop]
261 except KeyError:
262 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
263
264 def GetVendorBuildProp(self, prop):
265 """Returns the inquired vendor build property."""
266 try:
267 return self.info_dict.get("vendor.build.prop", {})[prop]
268 except KeyError:
269 raise common.ExternalError(
270 "couldn't find %s in vendor.build.prop" % (prop,))
271
272 def GetOemProperty(self, key):
273 if self.oem_props is not None and key in self.oem_props:
274 return self.oem_dicts[0][key]
275 return self.GetBuildProp(key)
276
277 def CalculateFingerprint(self):
278 if self.oem_props is None:
279 return self.GetBuildProp("ro.build.fingerprint")
280 return "%s/%s/%s:%s" % (
281 self.GetOemProperty("ro.product.brand"),
282 self.GetOemProperty("ro.product.name"),
283 self.GetOemProperty("ro.product.device"),
284 self.GetBuildProp("ro.build.thumbprint"))
285
286 def WriteMountOemScript(self, script):
287 assert self.oem_props is not None
288 recovery_mount_options = self.info_dict.get("recovery_mount_options")
289 script.Mount("/oem", recovery_mount_options)
290
291 def WriteDeviceAssertions(self, script, oem_no_mount):
292 # Read the property directly if not using OEM properties.
293 if not self.oem_props:
294 script.AssertDevice(self.device)
295 return
296
297 # Otherwise assert OEM properties.
298 if not self.oem_dicts:
299 raise common.ExternalError(
300 "No OEM file provided to answer expected assertions")
301
302 for prop in self.oem_props.split():
303 values = []
304 for oem_dict in self.oem_dicts:
305 if prop in oem_dict:
306 values.append(oem_dict[prop])
307 if not values:
308 raise common.ExternalError(
309 "The OEM file is missing the property %s" % (prop,))
310 script.AssertOemProperty(prop, values, oem_no_mount)
311
312
Doug Zongkereef39442009-04-02 12:14:19 -0700313def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200314 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700315
Doug Zongker951495f2009-08-14 12:44:19 -0700316 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
317 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700318
319
Tao Bao481bab82017-12-21 11:23:09 -0800320def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800321 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800322 if not oem_source:
323 return None
324
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800325 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800326 for oem_file in oem_source:
327 with open(oem_file) as fp:
328 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800329 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700330
Doug Zongkereef39442009-04-02 12:14:19 -0700331
Tao Baod42e97e2016-11-30 12:11:57 -0800332def _WriteRecoveryImageToBoot(script, output_zip):
333 """Find and write recovery image to /boot in two-step OTA.
334
335 In two-step OTAs, we write recovery image to /boot as the first step so that
336 we can reboot to there and install a new recovery image to /recovery.
337 A special "recovery-two-step.img" will be preferred, which encodes the correct
338 path of "/boot". Otherwise the device may show "device is corrupt" message
339 when booting into /boot.
340
341 Fall back to using the regular recovery.img if the two-step recovery image
342 doesn't exist. Note that rebuilding the special image at this point may be
343 infeasible, because we don't have the desired boot signer and keys when
344 calling ota_from_target_files.py.
345 """
346
347 recovery_two_step_img_name = "recovery-two-step.img"
348 recovery_two_step_img_path = os.path.join(
349 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
350 if os.path.exists(recovery_two_step_img_path):
351 recovery_two_step_img = common.GetBootableImage(
352 recovery_two_step_img_name, recovery_two_step_img_name,
353 OPTIONS.input_tmp, "RECOVERY")
354 common.ZipWriteStr(
355 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800356 print("two-step package: using %s in stage 1/3" % (
357 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800358 script.WriteRawImage("/boot", recovery_two_step_img_name)
359 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800360 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800361 # The "recovery.img" entry has been written into package earlier.
362 script.WriteRawImage("/boot", "recovery.img")
363
364
Doug Zongkerc9253822014-02-04 12:17:58 -0800365def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700366 namelist = [name for name in target_files_zip.namelist()]
367 return ("SYSTEM/recovery-from-boot.p" in namelist or
368 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700369
Tao Bao457cbf62017-03-06 09:56:01 -0800370
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700371def HasVendorPartition(target_files_zip):
372 try:
373 target_files_zip.getinfo("VENDOR/")
374 return True
375 except KeyError:
376 return False
377
Tao Bao457cbf62017-03-06 09:56:01 -0800378
Tao Bao481bab82017-12-21 11:23:09 -0800379def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700380 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800381 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700382
383
Tao Bao481bab82017-12-21 11:23:09 -0800384def WriteFingerprintAssertion(script, target_info, source_info):
385 source_oem_props = source_info.oem_props
386 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700387
Tao Bao481bab82017-12-21 11:23:09 -0800388 if source_oem_props is None and target_oem_props is None:
389 script.AssertSomeFingerprint(
390 source_info.fingerprint, target_info.fingerprint)
391 elif source_oem_props is not None and target_oem_props is not None:
392 script.AssertSomeThumbprint(
393 target_info.GetBuildProp("ro.build.thumbprint"),
394 source_info.GetBuildProp("ro.build.thumbprint"))
395 elif source_oem_props is None and target_oem_props is not None:
396 script.AssertFingerprintOrThumbprint(
397 source_info.fingerprint,
398 target_info.GetBuildProp("ro.build.thumbprint"))
399 else:
400 script.AssertFingerprintOrThumbprint(
401 target_info.fingerprint,
402 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700403
Doug Zongkerfc44a512014-08-26 13:10:25 -0700404
Tao Bao7e0f1602017-03-06 15:50:08 -0800405def GetImage(which, tmpdir):
406 """Returns an image object suitable for passing to BlockImageDiff.
407
408 'which' partition must be "system" or "vendor". A prebuilt image and file
409 map must already exist in tmpdir.
410 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700411
412 assert which in ("system", "vendor")
413
414 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700415 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700416
Tao Bao7e0f1602017-03-06 15:50:08 -0800417 # The image and map files must have been created prior to calling
418 # ota_from_target_files.py (since LMP).
419 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700420
Tao Baoff777812015-05-12 11:42:31 -0700421 # Bug: http://b/20939131
422 # In ext4 filesystems, block 0 might be changed even being mounted
423 # R/O. We add it to clobbered_blocks so that it will be written to the
424 # target unconditionally. Note that they are still part of care_map.
425 clobbered_blocks = "0"
426
427 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700428
429
Tao Bao481bab82017-12-21 11:23:09 -0800430def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
431 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700432 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700433
434 Metadata used for on-device compatibility verification is retrieved from
435 target_zip then added to compatibility.zip which is added to the output_zip
436 archive.
437
Tao Baobcd1d162017-08-26 13:10:26 -0700438 Compatibility archive should only be included for devices that have enabled
439 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700440
441 Args:
442 target_zip: Zip file containing the source files to be included for OTA.
443 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800444 target_info: The BuildInfo instance that holds the target build info.
445 source_info: The BuildInfo instance that holds the source build info, if
446 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700447 """
448
Tao Baobcd1d162017-08-26 13:10:26 -0700449 def AddCompatibilityArchive(system_updated, vendor_updated):
450 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700451
Tao Baobcd1d162017-08-26 13:10:26 -0700452 Args:
453 system_updated: If True, the system image will be updated and therefore
454 its metadata should be included.
455 vendor_updated: If True, the vendor image will be updated and therefore
456 its metadata should be included.
457 """
458 # Determine what metadata we need. Files are names relative to META/.
459 compatibility_files = []
460 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
461 system_metadata = ("system_manifest.xml", "system_matrix.xml")
462 if vendor_updated:
463 compatibility_files += vendor_metadata
464 if system_updated:
465 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700466
Tao Baobcd1d162017-08-26 13:10:26 -0700467 # Create new archive.
468 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800469 compatibility_archive_zip = zipfile.ZipFile(
470 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700471
Tao Baobcd1d162017-08-26 13:10:26 -0700472 # Add metadata.
473 for file_name in compatibility_files:
474 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700475
Tao Baobcd1d162017-08-26 13:10:26 -0700476 if target_file_name in target_zip.namelist():
477 data = target_zip.read(target_file_name)
478 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700479
Tao Baobcd1d162017-08-26 13:10:26 -0700480 # Ensure files are written before we copy into output_zip.
481 compatibility_archive_zip.close()
482
483 # Only add the archive if we have any compatibility info.
484 if compatibility_archive_zip.namelist():
485 common.ZipWrite(output_zip, compatibility_archive.name,
486 arcname="compatibility.zip",
487 compress_type=zipfile.ZIP_STORED)
488
489 # Will only proceed if the target has enabled the Treble support (as well as
490 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800491 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700492 return
493
494 # We don't support OEM thumbprint in Treble world (which calculates
495 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800496 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700497
498 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800499 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700500 AddCompatibilityArchive(True, True)
501 return
502
Tao Bao481bab82017-12-21 11:23:09 -0800503 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700504
Tao Bao481bab82017-12-21 11:23:09 -0800505 source_fp = source_info.fingerprint
506 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700507 system_updated = source_fp != target_fp
508
Tao Bao481bab82017-12-21 11:23:09 -0800509 source_fp_vendor = source_info.GetVendorBuildProp(
510 "ro.vendor.build.fingerprint")
511 target_fp_vendor = target_info.GetVendorBuildProp(
512 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700513 vendor_updated = source_fp_vendor != target_fp_vendor
514
515 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700516
517
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700518def WriteFullOTAPackage(input_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800519 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700520
Tao Bao481bab82017-12-21 11:23:09 -0800521 # We don't know what version it will be installed on top of. We expect the API
522 # just won't change very often. Similarly for fstab, it might have changed in
523 # the target build.
524 target_api_version = target_info["recovery_api_version"]
525 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700526
Tao Bao481bab82017-12-21 11:23:09 -0800527 if target_info.oem_props and not OPTIONS.oem_no_mount:
528 target_info.WriteMountOemScript(script)
529
Dan Albert8b72aef2015-03-23 19:13:21 -0700530 metadata = {
Tao Bao481bab82017-12-21 11:23:09 -0800531 "post-build": target_info.fingerprint,
532 "pre-device": target_info.device,
533 "post-timestamp": target_info.GetBuildProp("ro.build.date.utc"),
534 "ota-type" : "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700535 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700536
Doug Zongker05d3dea2009-06-22 11:32:31 -0700537 device_specific = common.DeviceSpecificParams(
538 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800539 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700540 output_zip=output_zip,
541 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700542 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700543 metadata=metadata,
544 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700545
Tao Bao457cbf62017-03-06 09:56:01 -0800546 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800547
Tao Bao481bab82017-12-21 11:23:09 -0800548 # Assertions (e.g. downgrade check, device properties check).
549 ts = target_info.GetBuildProp("ro.build.date.utc")
550 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700551 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700552
Tao Bao481bab82017-12-21 11:23:09 -0800553 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700554 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800555
556 # Two-step package strategy (in chronological order, which is *not*
557 # the order in which the generated script has things):
558 #
559 # if stage is not "2/3" or "3/3":
560 # write recovery image to boot partition
561 # set stage to "2/3"
562 # reboot to boot partition and restart recovery
563 # else if stage is "2/3":
564 # write recovery image to recovery partition
565 # set stage to "3/3"
566 # reboot to recovery partition and restart recovery
567 # else:
568 # (stage must be "3/3")
569 # set stage to ""
570 # do normal full package installation:
571 # wipe and install system, boot image, etc.
572 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700573 # complete script normally
574 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800575
576 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
577 OPTIONS.input_tmp, "RECOVERY")
578 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800579 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800580 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800581 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800582 assert fs.fs_type.upper() == "EMMC", \
583 "two-step packages only supported on devices with EMMC /misc partitions"
584 bcb_dev = {"bcb_dev": fs.device}
585 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
586 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700587if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800588""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800589
590 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
591 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800592 script.WriteRawImage("/recovery", "recovery.img")
593 script.AppendExtra("""
594set_stage("%(bcb_dev)s", "3/3");
595reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700596else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800597""" % bcb_dev)
598
Tao Baod42e97e2016-11-30 12:11:57 -0800599 # Stage 3/3: Make changes.
600 script.Comment("Stage 3/3")
601
Tao Bao6c55a8a2015-04-08 15:30:27 -0700602 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800603 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700604
Doug Zongkere5ff5902012-01-17 10:55:37 -0800605 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700606
Doug Zongker01ce19c2014-02-04 13:48:15 -0800607 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700608
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700609 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800610 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700611 if HasVendorPartition(input_zip):
612 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700613
Doug Zongker4b9596f2014-06-09 14:15:45 -0700614 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800615
Tao Bao457cbf62017-03-06 09:56:01 -0800616 # Full OTA is done as an "incremental" against an empty source image. This
617 # has the effect of writing new data from the package to the entire
618 # partition, but lets us reuse the updater code that writes incrementals to
619 # do it.
620 system_tgt = GetImage("system", OPTIONS.input_tmp)
621 system_tgt.ResetFileMap()
622 system_diff = common.BlockDifference("system", system_tgt, src=None)
623 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700624
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700625 boot_img = common.GetBootableImage(
626 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800627
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700628 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700629 script.ShowProgress(0.1, 0)
630
Tao Bao457cbf62017-03-06 09:56:01 -0800631 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
632 vendor_tgt.ResetFileMap()
633 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
634 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700635
Tao Bao481bab82017-12-21 11:23:09 -0800636 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700637
Tao Bao481bab82017-12-21 11:23:09 -0800638 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700639 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700640
Doug Zongker01ce19c2014-02-04 13:48:15 -0800641 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700642 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700643
Doug Zongker01ce19c2014-02-04 13:48:15 -0800644 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700645 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700646
Doug Zongker1c390a22009-05-14 19:06:36 -0700647 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700648 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700649
Doug Zongker14833602010-02-02 13:12:04 -0800650 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800651
Doug Zongker922206e2014-03-04 13:16:24 -0800652 if OPTIONS.wipe_user_data:
653 script.ShowProgress(0.1, 10)
654 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700655
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800656 if OPTIONS.two_step:
657 script.AppendExtra("""
658set_stage("%(bcb_dev)s", "");
659""" % bcb_dev)
660 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800661
662 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
663 script.Comment("Stage 1/3")
664 _WriteRecoveryImageToBoot(script, output_zip)
665
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800666 script.AppendExtra("""
667set_stage("%(bcb_dev)s", "2/3");
668reboot_now("%(bcb_dev)s", "");
669endif;
670endif;
671""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800672
Tao Bao5d182562016-02-23 11:38:39 -0800673 script.SetProgress(1)
674 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800675 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700676 WriteMetadata(metadata, output_zip)
677
Doug Zongkerfc44a512014-08-26 13:10:25 -0700678
Doug Zongker2ea21062010-04-28 16:05:21 -0700679def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800680 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
681 common.ZipWriteStr(output_zip, METADATA_NAME, value,
682 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700683
Doug Zongkerfc44a512014-08-26 13:10:25 -0700684
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700685def GetBuildProp(prop, info_dict):
Tao Baobcd1d162017-08-26 13:10:26 -0700686 """Returns the inquired build property from a given info_dict."""
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700687 try:
688 return info_dict.get("build.prop", {})[prop]
689 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700690 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700691
Doug Zongkerfc44a512014-08-26 13:10:25 -0700692
Tao Baobcd1d162017-08-26 13:10:26 -0700693def GetVendorBuildProp(prop, info_dict):
694 """Returns the inquired vendor build property from a given info_dict."""
695 try:
696 return info_dict.get("vendor.build.prop", {})[prop]
697 except KeyError:
698 raise common.ExternalError(
699 "couldn't find %s in vendor.build.prop" % (prop,))
700
701
Tao Bao481bab82017-12-21 11:23:09 -0800702def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800703 # Only incremental OTAs are allowed to reach here.
704 assert OPTIONS.incremental_source is not None
705
Tao Bao481bab82017-12-21 11:23:09 -0800706 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
707 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800708 is_downgrade = long(post_timestamp) < long(pre_timestamp)
709
710 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800711 if not is_downgrade:
712 raise RuntimeError("--downgrade specified but no downgrade detected: "
713 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800714 metadata["ota-downgrade"] = "yes"
715 elif OPTIONS.timestamp:
716 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800717 raise RuntimeError("--override_timestamp specified but no timestamp hack "
718 "needed: pre: %s, post: %s" % (pre_timestamp,
719 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800720 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800721 else:
722 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800723 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800724 "pre: %s, post: %s. Need to specify "
725 "--override_timestamp OR --downgrade to allow "
726 "building the incremental." % (pre_timestamp,
727 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800728 metadata["post-timestamp"] = post_timestamp
729
730
Geremy Condra36bd3652014-02-06 19:45:10 -0800731def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800732 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
733 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800734
Tao Bao481bab82017-12-21 11:23:09 -0800735 target_api_version = target_info["recovery_api_version"]
736 source_api_version = source_info["recovery_api_version"]
737 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700738 print("WARNING: generating edify script for a source that "
739 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800740
Tao Bao481bab82017-12-21 11:23:09 -0800741 script = edify_generator.EdifyGenerator(
742 source_api_version, target_info, fstab=source_info["fstab"])
743
744 if target_info.oem_props or source_info.oem_props:
745 if not OPTIONS.oem_no_mount:
746 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -0700747
Dan Albert8b72aef2015-03-23 19:13:21 -0700748 metadata = {
Tao Bao481bab82017-12-21 11:23:09 -0800749 "pre-device": source_info.device,
Tao Baod8d14be2016-02-04 14:26:02 -0800750 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700751 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800752
Tao Bao481bab82017-12-21 11:23:09 -0800753 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -0800754
Geremy Condra36bd3652014-02-06 19:45:10 -0800755 device_specific = common.DeviceSpecificParams(
756 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800757 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800758 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800759 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800760 output_zip=output_zip,
761 script=script,
762 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -0800763 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800764
Tao Bao481bab82017-12-21 11:23:09 -0800765 metadata["pre-build"] = source_info.fingerprint
766 metadata["post-build"] = target_info.fingerprint
767 metadata["pre-build-incremental"] = source_info.GetBuildProp(
768 "ro.build.version.incremental")
769 metadata["post-build-incremental"] = target_info.GetBuildProp(
770 "ro.build.version.incremental")
Geremy Condra36bd3652014-02-06 19:45:10 -0800771
772 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800773 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800774 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800775 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800776 updating_boot = (not OPTIONS.two_step and
777 (source_boot.data != target_boot.data))
778
Geremy Condra36bd3652014-02-06 19:45:10 -0800779 target_recovery = common.GetBootableImage(
780 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800781
Tao Bao7e0f1602017-03-06 15:50:08 -0800782 system_src = GetImage("system", OPTIONS.source_tmp)
783 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700784
Tao Bao0582cb62017-12-21 11:47:01 -0800785 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -0800786 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -0800787 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -0700788
Tao Baof8acad12016-07-07 09:09:58 -0700789 # Check the first block of the source system partition for remount R/W only
790 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -0800791 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700792 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700793 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
794 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
795 # b) the blocks listed in block map may not contain all the bytes for a given
796 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -0800797 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700798 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
799 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700800 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800801 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700802 version=blockimgdiff_version,
803 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700804
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700805 if HasVendorPartition(target_zip):
806 if not HasVendorPartition(source_zip):
807 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800808 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
809 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800810
811 # Check first block of vendor partition for remount R/W only if
812 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -0800813 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800814 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700815 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700816 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800817 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700818 version=blockimgdiff_version,
819 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700820 else:
821 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800822
Tao Baobcd1d162017-08-26 13:10:26 -0700823 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -0800824 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700825
Tao Bao481bab82017-12-21 11:23:09 -0800826 # Assertions (e.g. device properties check).
827 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -0800828 device_specific.IncrementalOTA_Assertions()
829
830 # Two-step incremental package strategy (in chronological order,
831 # which is *not* the order in which the generated script has
832 # things):
833 #
834 # if stage is not "2/3" or "3/3":
835 # do verification on current system
836 # write recovery image to boot partition
837 # set stage to "2/3"
838 # reboot to boot partition and restart recovery
839 # else if stage is "2/3":
840 # write recovery image to recovery partition
841 # set stage to "3/3"
842 # reboot to recovery partition and restart recovery
843 # else:
844 # (stage must be "3/3")
845 # perform update:
846 # patch system files, etc.
847 # force full install of new boot image
848 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700849 # complete script normally
850 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800851
852 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800853 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -0800854 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700855 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800856 assert fs.fs_type.upper() == "EMMC", \
857 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -0800858 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -0800859 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
860 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700861if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800862""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800863
864 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
865 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700866 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800867 script.WriteRawImage("/recovery", "recovery.img")
868 script.AppendExtra("""
869set_stage("%(bcb_dev)s", "3/3");
870reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700871else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800872""" % bcb_dev)
873
Tao Baod42e97e2016-11-30 12:11:57 -0800874 # Stage 1/3: (a) Verify the current system.
875 script.Comment("Stage 1/3")
876
Tao Bao6c55a8a2015-04-08 15:30:27 -0700877 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800878 script.Print("Source: {}".format(source_info.fingerprint))
879 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700880
Geremy Condra36bd3652014-02-06 19:45:10 -0800881 script.Print("Verifying current system...")
882
883 device_specific.IncrementalOTA_VerifyBegin()
884
Tao Bao481bab82017-12-21 11:23:09 -0800885 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800886
Tao Baod8d14be2016-02-04 14:26:02 -0800887 # Check the required cache size (i.e. stashed blocks).
888 size = []
889 if system_diff:
890 size.append(system_diff.required_cache)
891 if vendor_diff:
892 size.append(vendor_diff.required_cache)
893
Geremy Condra36bd3652014-02-06 19:45:10 -0800894 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -0800895 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800896 d = common.Difference(target_boot, source_boot)
897 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700898 if d is None:
899 include_full_boot = True
900 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
901 else:
902 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800903
Tao Bao89fbb0f2017-01-10 10:47:58 -0800904 print("boot target: %d source: %d diff: %d" % (
905 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800906
Doug Zongkerf8340082014-08-05 10:39:37 -0700907 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800908
Doug Zongkerf8340082014-08-05 10:39:37 -0700909 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
910 (boot_type, boot_device,
911 source_boot.size, source_boot.sha1,
912 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800913 size.append(target_boot.size)
914
915 if size:
916 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800917
918 device_specific.IncrementalOTA_VerifyEnd()
919
920 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800921 # Stage 1/3: (b) Write recovery image to /boot.
922 _WriteRecoveryImageToBoot(script, output_zip)
923
Geremy Condra36bd3652014-02-06 19:45:10 -0800924 script.AppendExtra("""
925set_stage("%(bcb_dev)s", "2/3");
926reboot_now("%(bcb_dev)s", "");
927else
928""" % bcb_dev)
929
Tao Baod42e97e2016-11-30 12:11:57 -0800930 # Stage 3/3: Make changes.
931 script.Comment("Stage 3/3")
932
Jesse Zhao75bcea02015-01-06 10:59:53 -0800933 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700934 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800935 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700936 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800937
Geremy Condra36bd3652014-02-06 19:45:10 -0800938 script.Comment("---- start making changes here ----")
939
940 device_specific.IncrementalOTA_InstallBegin()
941
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700942 system_diff.WriteScript(script, output_zip,
943 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700944
Doug Zongkerfc44a512014-08-26 13:10:25 -0700945 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700946 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800947
948 if OPTIONS.two_step:
949 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
950 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800951 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800952
953 if not OPTIONS.two_step:
954 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700955 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800956 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700957 script.Print("Installing boot image...")
958 script.WriteRawImage("/boot", "boot.img")
959 else:
960 # Produce the boot image by applying a patch to the current
961 # contents of the boot partition, and write it back to the
962 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -0800963 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700964 script.Print("Patching boot image...")
965 script.ShowProgress(0.1, 10)
966 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
967 % (boot_type, boot_device,
968 source_boot.size, source_boot.sha1,
969 target_boot.size, target_boot.sha1),
970 "-",
971 target_boot.size, target_boot.sha1,
972 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800973 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800974 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800975
976 # Do device-specific installation (eg, write radio image).
977 device_specific.IncrementalOTA_InstallEnd()
978
979 if OPTIONS.extra_script is not None:
980 script.AppendExtra(OPTIONS.extra_script)
981
Doug Zongker922206e2014-03-04 13:16:24 -0800982 if OPTIONS.wipe_user_data:
983 script.Print("Erasing user data...")
984 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -0800985 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -0800986
Geremy Condra36bd3652014-02-06 19:45:10 -0800987 if OPTIONS.two_step:
988 script.AppendExtra("""
989set_stage("%(bcb_dev)s", "");
990endif;
991endif;
992""" % bcb_dev)
993
994 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -0800995 # For downgrade OTAs, we prefer to use the update-binary in the source
996 # build that is actually newer than the one in the target build.
997 if OPTIONS.downgrade:
998 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
999 else:
1000 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001001 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001002 WriteMetadata(metadata, output_zip)
1003
Doug Zongker32b527d2014-03-04 10:03:02 -08001004
Tao Baoc098e9e2016-01-07 13:03:56 -08001005def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1006 source_file=None):
1007 """Generate an Android OTA package that has A/B update payload."""
1008
Tao Bao2dd1c482017-02-03 16:49:39 -08001009 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1010 expected_length=None):
1011 """Compute the streaming metadata for a given zip.
1012
1013 When 'reserve_space' is True, we reserve extra space for the offset and
1014 length of the metadata entry itself, although we don't know the final
1015 values until the package gets signed. This function will be called again
1016 after signing. We then write the actual values and pad the string to the
1017 length we set earlier. Note that we can't use the actual length of the
1018 metadata entry in the second run. Otherwise the offsets for other entries
1019 will be changing again.
1020 """
Tao Baoc96316c2017-01-24 22:10:49 -08001021
1022 def ComputeEntryOffsetSize(name):
1023 """Compute the zip entry offset and size."""
1024 info = zip_file.getinfo(name)
1025 offset = info.header_offset + len(info.FileHeader())
1026 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001027 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001028
1029 # payload.bin and payload_properties.txt must exist.
1030 offsets = [ComputeEntryOffsetSize('payload.bin'),
1031 ComputeEntryOffsetSize('payload_properties.txt')]
1032
1033 # care_map.txt is available only if dm-verity is enabled.
1034 if 'care_map.txt' in zip_file.namelist():
1035 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001036
Tao Bao21803d32017-04-19 10:16:09 -07001037 if 'compatibility.zip' in zip_file.namelist():
1038 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1039
Tao Bao2dd1c482017-02-03 16:49:39 -08001040 # 'META-INF/com/android/metadata' is required. We don't know its actual
1041 # offset and length (as well as the values for other entries). So we
1042 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1043 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1044 # beginning of the zip), as well as the possible value changes in other
1045 # entries.
1046 if reserve_space:
1047 offsets.append('metadata:' + ' ' * 10)
1048 else:
1049 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1050
1051 value = ','.join(offsets)
1052 if expected_length is not None:
1053 assert len(value) <= expected_length, \
1054 'Insufficient reserved space: reserved=%d, actual=%d' % (
1055 expected_length, len(value))
1056 value += ' ' * (expected_length - len(value))
1057 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001058
Alex Deymod8d96ec2016-06-10 16:38:31 -07001059 # The place where the output from the subprocess should go.
1060 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1061
Tao Baodea0f8b2016-06-20 17:55:06 -07001062 # A/B updater expects a signing key in RSA format. Gets the key ready for
1063 # later use in step 3, unless a payload_signer has been specified.
1064 if OPTIONS.payload_signer is None:
1065 cmd = ["openssl", "pkcs8",
1066 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
Christian Oderf63e2cd2017-05-01 22:30:15 +02001067 "-inform", "DER"]
1068 pw = OPTIONS.key_passwords[OPTIONS.package_key]
1069 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
Tao Baodea0f8b2016-06-20 17:55:06 -07001070 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1071 cmd.extend(["-out", rsa_key])
Tao Bao481bab82017-12-21 11:23:09 -08001072 p1 = common.Run(cmd, verbose=False, stdout=log_file,
1073 stderr=subprocess.STDOUT)
Tao Bao6047c242016-06-21 13:35:26 -07001074 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001075 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001076
Tao Baodea0f8b2016-06-20 17:55:06 -07001077 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001078 temp_zip_file = tempfile.NamedTemporaryFile()
1079 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1080 compression=zipfile.ZIP_DEFLATED)
1081
Tao Bao481bab82017-12-21 11:23:09 -08001082 if source_file is not None:
1083 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1084 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1085 else:
1086 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1087 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001088
Tao Bao481bab82017-12-21 11:23:09 -08001089 # Metadata to comply with Android OTA package format.
Tao Baoc098e9e2016-01-07 13:03:56 -08001090 metadata = {
Tao Bao481bab82017-12-21 11:23:09 -08001091 "post-build" : target_info.fingerprint,
1092 "post-build-incremental" : target_info.GetBuildProp(
1093 "ro.build.version.incremental"),
1094 "ota-required-cache" : "0",
1095 "ota-type" : "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001096 }
1097
1098 if source_file is not None:
Tao Bao481bab82017-12-21 11:23:09 -08001099 metadata["pre-device"] = source_info.device
1100 metadata["pre-build"] = source_info.fingerprint
1101 metadata["pre-build-incremental"] = source_info.GetBuildProp(
1102 "ro.build.version.incremental")
Tao Baoc098e9e2016-01-07 13:03:56 -08001103
Tao Bao481bab82017-12-21 11:23:09 -08001104 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001105 else:
Tao Bao481bab82017-12-21 11:23:09 -08001106 metadata["pre-device"] = target_info.device
1107 metadata["post-timestamp"] = target_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -08001108
Tao Baoc098e9e2016-01-07 13:03:56 -08001109 # 1. Generate payload.
1110 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1111 cmd = ["brillo_update_payload", "generate",
1112 "--payload", payload_file,
1113 "--target_image", target_file]
1114 if source_file is not None:
1115 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001116 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1117 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001118 assert p1.returncode == 0, "brillo_update_payload generate failed"
1119
1120 # 2. Generate hashes of the payload and metadata files.
1121 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1122 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1123 cmd = ["brillo_update_payload", "hash",
1124 "--unsigned_payload", payload_file,
1125 "--signature_size", "256",
1126 "--metadata_hash_file", metadata_sig_file,
1127 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001128 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1129 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001130 assert p1.returncode == 0, "brillo_update_payload hash failed"
1131
1132 # 3. Sign the hashes and insert them back into the payload file.
1133 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1134 suffix=".bin")
1135 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1136 suffix=".bin")
1137 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001138 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001139 cmd = [OPTIONS.payload_signer]
1140 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001141 else:
1142 cmd = ["openssl", "pkeyutl", "-sign",
1143 "-inkey", rsa_key,
1144 "-pkeyopt", "digest:sha256"]
1145 cmd.extend(["-in", payload_sig_file,
1146 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001147 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1148 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001149 assert p1.returncode == 0, "openssl sign payload failed"
1150
1151 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001152 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001153 cmd = [OPTIONS.payload_signer]
1154 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001155 else:
1156 cmd = ["openssl", "pkeyutl", "-sign",
1157 "-inkey", rsa_key,
1158 "-pkeyopt", "digest:sha256"]
1159 cmd.extend(["-in", metadata_sig_file,
1160 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001161 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1162 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001163 assert p1.returncode == 0, "openssl sign metadata failed"
1164
1165 # 3c. Insert the signatures back into the payload file.
1166 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1167 suffix=".bin")
1168 cmd = ["brillo_update_payload", "sign",
1169 "--unsigned_payload", payload_file,
1170 "--payload", signed_payload_file,
1171 "--signature_size", "256",
1172 "--metadata_signature_file", signed_metadata_sig_file,
1173 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001174 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1175 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001176 assert p1.returncode == 0, "brillo_update_payload sign failed"
1177
Alex Deymo19241c12016-02-04 22:29:29 -08001178 # 4. Dump the signed payload properties.
1179 properties_file = common.MakeTempFile(prefix="payload-properties-",
1180 suffix=".txt")
1181 cmd = ["brillo_update_payload", "properties",
1182 "--payload", signed_payload_file,
1183 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001184 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1185 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001186 assert p1.returncode == 0, "brillo_update_payload properties failed"
1187
Tao Bao7c5dc572016-06-14 17:48:11 -07001188 if OPTIONS.wipe_user_data:
1189 with open(properties_file, "a") as f:
1190 f.write("POWERWASH=1\n")
1191 metadata["ota-wipe"] = "yes"
1192
Tao Baoc96316c2017-01-24 22:10:49 -08001193 # Add the signed payload file and properties into the zip. In order to
1194 # support streaming, we pack payload.bin, payload_properties.txt and
1195 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1196 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001197 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1198 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001199 common.ZipWrite(output_zip, properties_file,
1200 arcname="payload_properties.txt",
1201 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001202
Tianjie Xucfa86222016-03-07 16:31:19 -08001203 # If dm-verity is supported for the device, copy contents of care_map
1204 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001205 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001206 if (target_info.get("verity") == "true" or
1207 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001208 care_map_path = "META/care_map.txt"
1209 namelist = target_zip.namelist()
1210 if care_map_path in namelist:
1211 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001212 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001213 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001214 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001215 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001216
Tao Bao481bab82017-12-21 11:23:09 -08001217 # source_info must be None for full OTAs.
Tao Baobcd1d162017-08-26 13:10:26 -07001218 if source_file is None:
Tao Bao481bab82017-12-21 11:23:09 -08001219 assert source_info is None
Tao Bao21803d32017-04-19 10:16:09 -07001220
Tao Baobcd1d162017-08-26 13:10:26 -07001221 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001222 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001223
Tao Bao21803d32017-04-19 10:16:09 -07001224 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001225
Tao Bao2dd1c482017-02-03 16:49:39 -08001226 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001227 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001228 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001229 WriteMetadata(metadata, output_zip)
1230 common.ZipClose(output_zip)
1231
Tao Bao2dd1c482017-02-03 16:49:39 -08001232 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
Tao Bao89d7ab22017-12-14 17:05:33 -08001233 # ZIP entries, as well as padding the entry headers. We do a preliminary
Tao Bao2dd1c482017-02-03 16:49:39 -08001234 # signing (with an incomplete metadata entry) to allow that to happen. Then
Tao Bao89d7ab22017-12-14 17:05:33 -08001235 # compute the ZIP entry offsets, write back the final metadata and do the
Tao Bao2dd1c482017-02-03 16:49:39 -08001236 # final signing.
Tao Bao89d7ab22017-12-14 17:05:33 -08001237 prelim_signing = common.MakeTempFile(suffix='.zip')
1238 SignOutput(temp_zip_file.name, prelim_signing)
Tao Bao2dd1c482017-02-03 16:49:39 -08001239 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001240
Tao Bao2dd1c482017-02-03 16:49:39 -08001241 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Bao89d7ab22017-12-14 17:05:33 -08001242 prelim_signing_zip = zipfile.ZipFile(prelim_signing, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001243 expected_length = len(metadata['ota-streaming-property-files'])
1244 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao89d7ab22017-12-14 17:05:33 -08001245 prelim_signing_zip, reserve_space=False, expected_length=expected_length)
1246 common.ZipClose(prelim_signing_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001247
Tao Bao89d7ab22017-12-14 17:05:33 -08001248 # Replace the METADATA entry.
1249 common.ZipDelete(prelim_signing, METADATA_NAME)
1250 output_zip = zipfile.ZipFile(prelim_signing, 'a',
Tao Bao2dd1c482017-02-03 16:49:39 -08001251 compression=zipfile.ZIP_DEFLATED)
Tao Bao2dd1c482017-02-03 16:49:39 -08001252 WriteMetadata(metadata, output_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001253 common.ZipClose(output_zip)
1254
1255 # Re-sign the package after updating the metadata entry.
Tao Bao89d7ab22017-12-14 17:05:33 -08001256 SignOutput(prelim_signing, output_file)
Tao Bao2dd1c482017-02-03 16:49:39 -08001257
1258 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao89d7ab22017-12-14 17:05:33 -08001259 output_zip = zipfile.ZipFile(output_file, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001260 actual = metadata['ota-streaming-property-files'].strip()
1261 expected = ComputeStreamingMetadata(output_zip)
1262 assert actual == expected, \
1263 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001264 common.ZipClose(output_zip)
1265
Tao Baoc098e9e2016-01-07 13:03:56 -08001266
Doug Zongkereef39442009-04-02 12:14:19 -07001267def main(argv):
1268
1269 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001270 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001271 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001272 elif o in ("-i", "--incremental_from"):
1273 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001274 elif o == "--full_radio":
1275 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001276 elif o == "--full_bootloader":
1277 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001278 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001279 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001280 elif o == "--downgrade":
1281 OPTIONS.downgrade = True
1282 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001283 elif o == "--override_timestamp":
1284 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001285 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001286 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001287 elif o == "--oem_no_mount":
1288 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001289 elif o in ("-e", "--extra_script"):
1290 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001291 elif o in ("-t", "--worker_threads"):
1292 if a.isdigit():
1293 OPTIONS.worker_threads = int(a)
1294 else:
1295 raise ValueError("Cannot parse value %r for option %r - only "
1296 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001297 elif o in ("-2", "--two_step"):
1298 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001299 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001300 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001301 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001302 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001303 elif o == "--block":
1304 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001305 elif o in ("-b", "--binary"):
1306 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001307 elif o == "--stash_threshold":
1308 try:
1309 OPTIONS.stash_threshold = float(a)
1310 except ValueError:
1311 raise ValueError("Cannot parse value %r for option %r - expecting "
1312 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001313 elif o == "--log_diff":
1314 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001315 elif o == "--payload_signer":
1316 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001317 elif o == "--payload_signer_args":
1318 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001319 elif o == "--extracted_input_target_files":
1320 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001321 else:
1322 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001323 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001324
1325 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001326 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001327 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001328 "package_key=",
1329 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001330 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001331 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001332 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001333 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001334 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001335 "extra_script=",
1336 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001337 "two_step",
1338 "no_signing",
1339 "block",
1340 "binary=",
1341 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001342 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001343 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001344 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001345 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001346 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001347 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001348 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001349 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001350
1351 if len(args) != 2:
1352 common.Usage(__doc__)
1353 sys.exit(1)
1354
Tao Bao5d182562016-02-23 11:38:39 -08001355 if OPTIONS.downgrade:
1356 # Sanity check to enforce a data wipe.
1357 if not OPTIONS.wipe_user_data:
1358 raise ValueError("Cannot downgrade without a data wipe")
1359
1360 # We should only allow downgrading incrementals (as opposed to full).
1361 # Otherwise the device may go back from arbitrary build with this full
1362 # OTA package.
1363 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001364 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001365
Tao Bao3e6161a2017-02-28 11:48:48 -08001366 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1367 "Cannot have --downgrade AND --override_timestamp both"
1368
Tao Baoc098e9e2016-01-07 13:03:56 -08001369 # Load the dict file from the zip directly to have a peek at the OTA type.
1370 # For packages using A/B update, unzipping is not needed.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001371 if OPTIONS.extracted_input is not None:
Tao Bao481bab82017-12-21 11:23:09 -08001372 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input,
1373 OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001374 else:
1375 input_zip = zipfile.ZipFile(args[0], "r")
1376 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1377 common.ZipClose(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001378
Tao Bao481bab82017-12-21 11:23:09 -08001379 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1380
Tao Baoc098e9e2016-01-07 13:03:56 -08001381 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1382
Christian Oderf63e2cd2017-05-01 22:30:15 +02001383 # Use the default key to sign the package if not specified with package_key.
1384 # package_keys are needed on ab_updates, so always define them if an
1385 # ab_update is getting created.
1386 if not OPTIONS.no_signing or ab_update:
1387 if OPTIONS.package_key is None:
1388 OPTIONS.package_key = OPTIONS.info_dict.get(
1389 "default_system_dev_certificate",
1390 "build/target/product/security/testkey")
1391 # Get signing keys
1392 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1393
Tao Baoc098e9e2016-01-07 13:03:56 -08001394 if ab_update:
1395 if OPTIONS.incremental_source is not None:
1396 OPTIONS.target_info_dict = OPTIONS.info_dict
1397 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1398 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1399 common.ZipClose(source_zip)
1400
1401 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001402 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001403 common.DumpInfoDict(OPTIONS.info_dict)
1404
1405 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001406 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001407 common.DumpInfoDict(OPTIONS.source_info_dict)
1408
1409 WriteABOTAPackageWithBrilloScript(
1410 target_file=args[0],
1411 output_file=args[1],
1412 source_file=OPTIONS.incremental_source)
1413
Tao Bao89fbb0f2017-01-10 10:47:58 -08001414 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001415 return
1416
Doug Zongker1c390a22009-05-14 19:06:36 -07001417 if OPTIONS.extra_script is not None:
1418 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1419
Dan Willemsencea5cd22017-03-21 14:44:27 -07001420 if OPTIONS.extracted_input is not None:
1421 OPTIONS.input_tmp = OPTIONS.extracted_input
1422 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao481bab82017-12-21 11:23:09 -08001423 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp,
1424 OPTIONS.input_tmp)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001425 input_zip = zipfile.ZipFile(args[0], "r")
1426 else:
1427 print("unzipping target target-files...")
1428 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1429 args[0], UNZIP_PATTERN)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001430
Dan Willemsencea5cd22017-03-21 14:44:27 -07001431 OPTIONS.target_tmp = OPTIONS.input_tmp
1432 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001433
Doug Zongker37974732010-09-16 17:44:38 -07001434 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001435 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07001436 common.DumpInfoDict(OPTIONS.info_dict)
1437
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001438 # If the caller explicitly specified the device-specific extensions
1439 # path via -s/--device_specific, use that. Otherwise, use
1440 # META/releasetools.py if it is present in the target target_files.
1441 # Otherwise, take the path of the file from 'tool_extensions' in the
1442 # info dict and look for that in the local filesystem, relative to
1443 # the current directory.
1444
Doug Zongker37974732010-09-16 17:44:38 -07001445 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001446 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1447 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001448 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001449 OPTIONS.device_specific = from_input
1450 else:
1451 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1452
Doug Zongker37974732010-09-16 17:44:38 -07001453 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001454 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001455
Tao Baoc098e9e2016-01-07 13:03:56 -08001456 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001457 raise common.ExternalError(
1458 "--- target build has specified no recovery ---")
1459
Tao Bao767e3ac2015-11-10 12:19:19 -08001460 # Set up the output zip. Create a temporary zip file if signing is needed.
1461 if OPTIONS.no_signing:
1462 if os.path.exists(args[1]):
1463 os.unlink(args[1])
1464 output_zip = zipfile.ZipFile(args[1], "w",
1465 compression=zipfile.ZIP_DEFLATED)
1466 else:
1467 temp_zip_file = tempfile.NamedTemporaryFile()
1468 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1469 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001470
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001471 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001472 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001473 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001474 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001475 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001476
Tao Bao767e3ac2015-11-10 12:19:19 -08001477 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001478 if OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001479 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001480
Tao Bao32b80dc2018-01-08 22:50:47 -08001481 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001482 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001483 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001484 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001485 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001486 UNZIP_PATTERN)
Tao Bao767e3ac2015-11-10 12:19:19 -08001487 OPTIONS.target_info_dict = OPTIONS.info_dict
1488 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1489 OPTIONS.source_tmp)
1490 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001491 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001492 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao32b80dc2018-01-08 22:50:47 -08001493
1494 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
1495
1496 if OPTIONS.log_diff:
1497 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001498 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001499 target_files_diff.recursiveDiff(
1500 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001501
Tao Bao767e3ac2015-11-10 12:19:19 -08001502 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001503
Tao Bao767e3ac2015-11-10 12:19:19 -08001504 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001505 if not OPTIONS.no_signing:
1506 SignOutput(temp_zip_file.name, args[1])
1507 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001508
Tao Bao89fbb0f2017-01-10 10:47:58 -08001509 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001510
1511
1512if __name__ == '__main__':
1513 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001514 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001515 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001516 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001517 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001518 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001519 finally:
1520 common.Cleanup()