blob: dfaa9c0ed72f49ca88b38ca809422c7fb6e782b5 [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 Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080053 -o (--oem_settings) <main_file[,additional_files...]>
54 Comma seperated list of files used to specify the expected OEM-specific
55 properties on the OEM partition of the intended device.
56 Multiple expected values can be used by providing multiple files.
57
Tao Bao8608cde2016-02-25 19:49:55 -080058 --oem_no_mount
59 For devices with OEM-specific properties but without an OEM partition,
60 do not mount the OEM partition in the updater-script. This should be
61 very rarely used, since it's expected to have a dedicated OEM partition
62 for OEM-specific properties. Only meaningful when -o is specified.
63
Doug Zongkerdbfaae52009-04-21 17:12:54 -070064 -w (--wipe_user_data)
65 Generate an OTA package that will wipe the user data partition
66 when installed.
67
Tao Bao5d182562016-02-23 11:38:39 -080068 --downgrade
69 Intentionally generate an incremental OTA that updates from a newer
70 build to an older one (based on timestamp comparison). "post-timestamp"
71 will be replaced by "ota-downgrade=yes" in the metadata file. A data
72 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080073 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080074 the OTA package, unless --binary flag is specified. Please also check the
75 doc for --override_timestamp below.
76
77 --override_timestamp
78 Intentionally generate an incremental OTA that updates from a newer
79 build to an older one (based on timestamp comparison), by overriding the
80 timestamp in package metadata. This differs from --downgrade flag: we
81 know for sure this is NOT an actual downgrade case, but two builds are
82 cut in a reverse order. A legit use case is that we cut a new build C
83 (after having A and B), but want to enfore an update path of A -> C -> B.
84 Specifying --downgrade may not help since that would enforce a data wipe
85 for C -> B update. The value of "post-timestamp" will be set to the newer
86 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080087
Doug Zongker1c390a22009-05-14 19:06:36 -070088 -e (--extra_script) <file>
89 Insert the contents of file at the end of the update script.
90
Doug Zongker9b23f2c2013-11-25 14:44:12 -080091 -2 (--two_step)
92 Generate a 'two-step' OTA package, where recovery is updated
93 first, so that any changes made to the system partition are done
94 using the new recovery (new kernel, etc.).
95
Doug Zongker26e66192014-02-20 13:22:07 -080096 --block
Tao Bao457cbf62017-03-06 09:56:01 -080097 Generate a block-based OTA for non-A/B device. We have deprecated the
98 support for file-based OTA since O. Block-based OTA will be used by
99 default for all non-A/B devices. Keeping this flag here to not break
100 existing callers.
Doug Zongker26e66192014-02-20 13:22:07 -0800101
Doug Zongker25568482014-03-03 10:21:27 -0800102 -b (--binary) <file>
103 Use the given binary as the update-binary in the output package,
104 instead of the binary in the build's target_files. Use for
105 development only.
106
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200107 -t (--worker_threads) <int>
108 Specifies the number of worker-threads that will be used when
109 generating patches for incremental updates (defaults to 3).
110
Tao Bao8dcf7382015-05-21 14:09:49 -0700111 --stash_threshold <float>
112 Specifies the threshold that will be used to compute the maximum
113 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800114
115 --gen_verify
116 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800117
118 --log_diff <file>
119 Generate a log file that shows the differences in the source and target
120 builds for an incremental package. This option is only meaningful when
121 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700122
123 --payload_signer <signer>
124 Specify the signer when signing the payload and metadata for A/B OTAs.
125 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
126 with the package private key. If the private key cannot be accessed
127 directly, a payload signer that knows how to do that should be specified.
128 The signer will be supplied with "-inkey <path_to_key>",
129 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700130
131 --payload_signer_args <args>
132 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700133"""
134
Tao Bao89fbb0f2017-01-10 10:47:58 -0800135from __future__ import print_function
136
Doug Zongkereef39442009-04-02 12:14:19 -0700137import sys
138
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800139if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800140 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700141 sys.exit(1)
142
Tao Bao2dd1c482017-02-03 16:49:39 -0800143import copy
Doug Zongkerfc44a512014-08-26 13:10:25 -0700144import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800145import os.path
Tao Baoc098e9e2016-01-07 13:03:56 -0800146import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700147import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700148import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700149import zipfile
150
151import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700152import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700153import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700154
155OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700156OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700157OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700158OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700159OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700160OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800161OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800162OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700163OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700164OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
165if OPTIONS.worker_threads == 0:
166 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800167OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900168OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800169OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800170OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700171OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800172OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700173OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700174OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700175OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700176# Stash size cannot exceed cache_size * threshold.
177OPTIONS.cache_size = None
178OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800179OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800180OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700181OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700182OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700183OPTIONS.extracted_input = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700184
Tao Bao2dd1c482017-02-03 16:49:39 -0800185METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800186UNZIP_PATTERN = ['IMAGES/*', 'META/*']
187
Tao Bao2dd1c482017-02-03 16:49:39 -0800188
Doug Zongkereef39442009-04-02 12:14:19 -0700189def SignOutput(temp_zip_name, output_zip_name):
190 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
191 pw = key_passwords[OPTIONS.package_key]
192
Doug Zongker951495f2009-08-14 12:44:19 -0700193 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
194 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700195
196
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800197def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700198 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700199 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700200 device = GetBuildProp("ro.product.device", info_dict)
201 script.AssertDevice(device)
202 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800203 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700204 raise common.ExternalError(
205 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700206 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800207 values = []
208 for oem_dict in oem_dicts:
209 if oem_dict.get(prop):
210 values.append(oem_dict[prop])
211 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700212 raise common.ExternalError(
213 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800214 script.AssertOemProperty(prop, values)
215
216
Tao Baoebce6972017-03-06 10:22:20 -0800217def _LoadOemDicts(script, recovery_mount_options=None):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800218 """Returns the list of loaded OEM properties dict."""
219 oem_dicts = None
220 if OPTIONS.oem_source is None:
221 raise common.ExternalError("OEM source required for this build")
Tao Baoebce6972017-03-06 10:22:20 -0800222 if not OPTIONS.oem_no_mount and script:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800223 script.Mount("/oem", recovery_mount_options)
224 oem_dicts = []
225 for oem_file in OPTIONS.oem_source:
226 oem_dicts.append(common.LoadDictionaryFromLines(
227 open(oem_file).readlines()))
228 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700229
Doug Zongkereef39442009-04-02 12:14:19 -0700230
Tao Baod42e97e2016-11-30 12:11:57 -0800231def _WriteRecoveryImageToBoot(script, output_zip):
232 """Find and write recovery image to /boot in two-step OTA.
233
234 In two-step OTAs, we write recovery image to /boot as the first step so that
235 we can reboot to there and install a new recovery image to /recovery.
236 A special "recovery-two-step.img" will be preferred, which encodes the correct
237 path of "/boot". Otherwise the device may show "device is corrupt" message
238 when booting into /boot.
239
240 Fall back to using the regular recovery.img if the two-step recovery image
241 doesn't exist. Note that rebuilding the special image at this point may be
242 infeasible, because we don't have the desired boot signer and keys when
243 calling ota_from_target_files.py.
244 """
245
246 recovery_two_step_img_name = "recovery-two-step.img"
247 recovery_two_step_img_path = os.path.join(
248 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
249 if os.path.exists(recovery_two_step_img_path):
250 recovery_two_step_img = common.GetBootableImage(
251 recovery_two_step_img_name, recovery_two_step_img_name,
252 OPTIONS.input_tmp, "RECOVERY")
253 common.ZipWriteStr(
254 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800255 print("two-step package: using %s in stage 1/3" % (
256 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800257 script.WriteRawImage("/boot", recovery_two_step_img_name)
258 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800259 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800260 # The "recovery.img" entry has been written into package earlier.
261 script.WriteRawImage("/boot", "recovery.img")
262
263
Doug Zongkerc9253822014-02-04 12:17:58 -0800264def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700265 namelist = [name for name in target_files_zip.namelist()]
266 return ("SYSTEM/recovery-from-boot.p" in namelist or
267 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700268
Tao Bao457cbf62017-03-06 09:56:01 -0800269
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700270def HasVendorPartition(target_files_zip):
271 try:
272 target_files_zip.getinfo("VENDOR/")
273 return True
274 except KeyError:
275 return False
276
Tao Bao457cbf62017-03-06 09:56:01 -0800277
Michael Runge6e836112014-04-15 17:40:21 -0700278def GetOemProperty(name, oem_props, oem_dict, info_dict):
279 if oem_props is not None and name in oem_props:
280 return oem_dict[name]
281 return GetBuildProp(name, info_dict)
282
283
284def CalculateFingerprint(oem_props, oem_dict, info_dict):
285 if oem_props is None:
286 return GetBuildProp("ro.build.fingerprint", info_dict)
287 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700288 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
289 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
290 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
291 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700292
Doug Zongkerfc44a512014-08-26 13:10:25 -0700293
Tao Bao7e0f1602017-03-06 15:50:08 -0800294def GetImage(which, tmpdir):
295 """Returns an image object suitable for passing to BlockImageDiff.
296
297 'which' partition must be "system" or "vendor". A prebuilt image and file
298 map must already exist in tmpdir.
299 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700300
301 assert which in ("system", "vendor")
302
303 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700304 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700305
Tao Bao7e0f1602017-03-06 15:50:08 -0800306 # The image and map files must have been created prior to calling
307 # ota_from_target_files.py (since LMP).
308 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700309
Tao Baoff777812015-05-12 11:42:31 -0700310 # Bug: http://b/20939131
311 # In ext4 filesystems, block 0 might be changed even being mounted
312 # R/O. We add it to clobbered_blocks so that it will be written to the
313 # target unconditionally. Note that they are still part of care_map.
314 clobbered_blocks = "0"
315
316 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700317
318
Michael Schwartz853cd302017-04-10 13:27:19 -0700319def AddCompatibilityArchive(target_zip, output_zip, system_included=True,
320 vendor_included=True):
321 """Adds compatibility info from target files into the output zip.
322
323 Metadata used for on-device compatibility verification is retrieved from
324 target_zip then added to compatibility.zip which is added to the output_zip
325 archive.
326
327 Compatibility archive should only be included for devices with a vendor
328 partition as checking provides value when system and vendor are independently
329 versioned.
330
331 Args:
332 target_zip: Zip file containing the source files to be included for OTA.
333 output_zip: Zip file that will be sent for OTA.
334 system_included: If True, the system image will be updated and therefore
335 its metadata should be included.
336 vendor_included: If True, the vendor image will be updated and therefore
337 its metadata should be included.
338 """
339
340 # Determine what metadata we need. Files are names relative to META/.
341 compatibility_files = []
342 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
343 system_metadata = ("system_manifest.xml", "system_matrix.xml")
344 if vendor_included:
345 compatibility_files += vendor_metadata
346 if system_included:
347 compatibility_files += system_metadata
348
349 # Create new archive.
350 compatibility_archive = tempfile.NamedTemporaryFile()
351 compatibility_archive_zip = zipfile.ZipFile(compatibility_archive, "w",
352 compression=zipfile.ZIP_DEFLATED)
353
354 # Add metadata.
355 for file_name in compatibility_files:
356 target_file_name = "META/" + file_name
357
358 if target_file_name in target_zip.namelist():
359 data = target_zip.read(target_file_name)
360 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
361
362 # Ensure files are written before we copy into output_zip.
363 compatibility_archive_zip.close()
364
365 # Only add the archive if we have any compatibility info.
366 if compatibility_archive_zip.namelist():
367 common.ZipWrite(output_zip, compatibility_archive.name,
368 arcname="compatibility.zip",
369 compress_type=zipfile.ZIP_STORED)
370
371
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700372def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700373 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700374 # be installed on top of. For now, we expect the API just won't
375 # change very often. Similarly for fstab, it might have changed
376 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700377 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700378
Tao Bao838c68f2016-03-15 19:16:18 +0000379 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700380 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800381 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700382 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800383 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700384
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800385 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
386 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700387 metadata = {
Tao Bao39f3eaf2017-03-09 15:01:11 -0800388 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800389 "pre-device": GetOemProperty("ro.product.device", oem_props,
390 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700391 OPTIONS.info_dict),
392 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
393 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700394
Doug Zongker05d3dea2009-06-22 11:32:31 -0700395 device_specific = common.DeviceSpecificParams(
396 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700397 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700398 output_zip=output_zip,
399 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700400 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700401 metadata=metadata,
402 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700403
Tao Bao457cbf62017-03-06 09:56:01 -0800404 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800405
Tao Bao457cbf62017-03-06 09:56:01 -0800406 metadata["ota-type"] = "BLOCK"
Tao Baod8d14be2016-02-04 14:26:02 -0800407
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700408 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
409 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
410 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700411
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800412 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700413 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800414
415 # Two-step package strategy (in chronological order, which is *not*
416 # the order in which the generated script has things):
417 #
418 # if stage is not "2/3" or "3/3":
419 # write recovery image to boot partition
420 # set stage to "2/3"
421 # reboot to boot partition and restart recovery
422 # else if stage is "2/3":
423 # write recovery image to recovery partition
424 # set stage to "3/3"
425 # reboot to recovery partition and restart recovery
426 # else:
427 # (stage must be "3/3")
428 # set stage to ""
429 # do normal full package installation:
430 # wipe and install system, boot image, etc.
431 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700432 # complete script normally
433 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800434
435 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
436 OPTIONS.input_tmp, "RECOVERY")
437 if OPTIONS.two_step:
438 if not OPTIONS.info_dict.get("multistage_support", None):
439 assert False, "two-step packages not supported by this build"
440 fs = OPTIONS.info_dict["fstab"]["/misc"]
441 assert fs.fs_type.upper() == "EMMC", \
442 "two-step packages only supported on devices with EMMC /misc partitions"
443 bcb_dev = {"bcb_dev": fs.device}
444 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
445 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700446if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800447""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800448
449 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
450 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800451 script.WriteRawImage("/recovery", "recovery.img")
452 script.AppendExtra("""
453set_stage("%(bcb_dev)s", "3/3");
454reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700455else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800456""" % bcb_dev)
457
Tao Baod42e97e2016-11-30 12:11:57 -0800458 # Stage 3/3: Make changes.
459 script.Comment("Stage 3/3")
460
Tao Bao6c55a8a2015-04-08 15:30:27 -0700461 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700462 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700463
Doug Zongkere5ff5902012-01-17 10:55:37 -0800464 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700465
Doug Zongker01ce19c2014-02-04 13:48:15 -0800466 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700467
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700468 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800469 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700470 if HasVendorPartition(input_zip):
471 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700472
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400473 # Place a copy of file_contexts.bin into the OTA package which will be used
474 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700475 if "selinux_fc" in OPTIONS.info_dict:
476 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500477
Michael Runge7cd99ba2014-10-22 17:21:48 -0700478 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
479
Doug Zongker4b9596f2014-06-09 14:15:45 -0700480 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800481
Tao Bao457cbf62017-03-06 09:56:01 -0800482 # Full OTA is done as an "incremental" against an empty source image. This
483 # has the effect of writing new data from the package to the entire
484 # partition, but lets us reuse the updater code that writes incrementals to
485 # do it.
486 system_tgt = GetImage("system", OPTIONS.input_tmp)
487 system_tgt.ResetFileMap()
488 system_diff = common.BlockDifference("system", system_tgt, src=None)
489 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700490
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700491 boot_img = common.GetBootableImage(
492 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800493
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700494 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700495 script.ShowProgress(0.1, 0)
496
Tao Bao457cbf62017-03-06 09:56:01 -0800497 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
498 vendor_tgt.ResetFileMap()
499 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
500 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700501
Michael Schwartz853cd302017-04-10 13:27:19 -0700502 AddCompatibilityArchive(input_zip, output_zip)
503
Doug Zongker37974732010-09-16 17:44:38 -0700504 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700505 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700506
Doug Zongker01ce19c2014-02-04 13:48:15 -0800507 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700508 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700509
Doug Zongker01ce19c2014-02-04 13:48:15 -0800510 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700511 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700512
Doug Zongker1c390a22009-05-14 19:06:36 -0700513 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700514 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700515
Doug Zongker14833602010-02-02 13:12:04 -0800516 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800517
Doug Zongker922206e2014-03-04 13:16:24 -0800518 if OPTIONS.wipe_user_data:
519 script.ShowProgress(0.1, 10)
520 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700521
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800522 if OPTIONS.two_step:
523 script.AppendExtra("""
524set_stage("%(bcb_dev)s", "");
525""" % bcb_dev)
526 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800527
528 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
529 script.Comment("Stage 1/3")
530 _WriteRecoveryImageToBoot(script, output_zip)
531
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800532 script.AppendExtra("""
533set_stage("%(bcb_dev)s", "2/3");
534reboot_now("%(bcb_dev)s", "");
535endif;
536endif;
537""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800538
Tao Bao5d182562016-02-23 11:38:39 -0800539 script.SetProgress(1)
540 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800541 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700542 WriteMetadata(metadata, output_zip)
543
Doug Zongkerfc44a512014-08-26 13:10:25 -0700544
Dan Albert8e0178d2015-01-27 15:53:15 -0800545def WritePolicyConfig(file_name, output_zip):
546 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500547
Doug Zongker2ea21062010-04-28 16:05:21 -0700548
549def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800550 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
551 common.ZipWriteStr(output_zip, METADATA_NAME, value,
552 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700553
Doug Zongkerfc44a512014-08-26 13:10:25 -0700554
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700555def GetBuildProp(prop, info_dict):
556 """Return the fingerprint of the build of a given target-files info_dict."""
557 try:
558 return info_dict.get("build.prop", {})[prop]
559 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700560 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700561
Doug Zongkerfc44a512014-08-26 13:10:25 -0700562
Tao Baob31892e2017-02-07 11:21:17 -0800563def HandleDowngradeMetadata(metadata):
564 # Only incremental OTAs are allowed to reach here.
565 assert OPTIONS.incremental_source is not None
566
567 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
568 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
569 is_downgrade = long(post_timestamp) < long(pre_timestamp)
570
571 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800572 if not is_downgrade:
573 raise RuntimeError("--downgrade specified but no downgrade detected: "
574 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800575 metadata["ota-downgrade"] = "yes"
576 elif OPTIONS.timestamp:
577 if not is_downgrade:
578 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
579 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
580 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800581 else:
582 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800583 raise RuntimeError("Downgrade detected based on timestamp check: "
584 "pre: %s, post: %s. Need to specify --timestamp OR "
585 "--downgrade to allow building the incremental." % (
586 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800587 metadata["post-timestamp"] = post_timestamp
588
589
Geremy Condra36bd3652014-02-06 19:45:10 -0800590def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
591 source_version = OPTIONS.source_info_dict["recovery_api_version"]
592 target_version = OPTIONS.target_info_dict["recovery_api_version"]
593
594 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700595 print("WARNING: generating edify script for a source that "
596 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700597 script = edify_generator.EdifyGenerator(
598 source_version, OPTIONS.target_info_dict,
599 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800600
Tao Bao3806c232015-07-05 21:08:33 -0700601 recovery_mount_options = OPTIONS.source_info_dict.get(
602 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700603 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
604 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800605 oem_dicts = None
606 if source_oem_props and target_oem_props:
607 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700608
Dan Albert8b72aef2015-03-23 19:13:21 -0700609 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700610 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800611 oem_dicts and oem_dicts[0],
612 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800613 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700614 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800615
Tao Baob31892e2017-02-07 11:21:17 -0800616 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800617
Geremy Condra36bd3652014-02-06 19:45:10 -0800618 device_specific = common.DeviceSpecificParams(
619 source_zip=source_zip,
620 source_version=source_version,
621 target_zip=target_zip,
622 target_version=target_version,
623 output_zip=output_zip,
624 script=script,
625 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700626 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800627
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800628 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700629 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800630 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700631 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800632 metadata["pre-build"] = source_fp
633 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700634 metadata["pre-build-incremental"] = GetBuildProp(
635 "ro.build.version.incremental", OPTIONS.source_info_dict)
636 metadata["post-build-incremental"] = GetBuildProp(
637 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800638
639 source_boot = common.GetBootableImage(
640 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
641 OPTIONS.source_info_dict)
642 target_boot = common.GetBootableImage(
643 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
644 updating_boot = (not OPTIONS.two_step and
645 (source_boot.data != target_boot.data))
646
Geremy Condra36bd3652014-02-06 19:45:10 -0800647 target_recovery = common.GetBootableImage(
648 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800649
Tao Bao7e0f1602017-03-06 15:50:08 -0800650 system_src = GetImage("system", OPTIONS.source_tmp)
651 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700652
653 blockimgdiff_version = 1
654 if OPTIONS.info_dict:
655 blockimgdiff_version = max(
656 int(i) for i in
657 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
658
Tao Baof8acad12016-07-07 09:09:58 -0700659 # Check the first block of the source system partition for remount R/W only
660 # if the filesystem is ext4.
661 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
662 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700663 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
664 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
665 # b) the blocks listed in block map may not contain all the bytes for a given
666 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700667 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
668 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
669 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700670 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800671 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700672 version=blockimgdiff_version,
673 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700674
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700675 if HasVendorPartition(target_zip):
676 if not HasVendorPartition(source_zip):
677 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800678 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
679 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800680
681 # Check first block of vendor partition for remount R/W only if
682 # disk type is ext4
683 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800684 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700685 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700686 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800687 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700688 version=blockimgdiff_version,
689 disable_imgdiff=disable_imgdiff)
Michael Schwartz853cd302017-04-10 13:27:19 -0700690
691 AddCompatibilityArchive(target_zip, output_zip,
692 system_src.TotalSha1() != system_tgt.TotalSha1(),
693 vendor_src.TotalSha1() != vendor_tgt.TotalSha1())
Doug Zongkerfc44a512014-08-26 13:10:25 -0700694 else:
695 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800696
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800697 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800698 device_specific.IncrementalOTA_Assertions()
699
700 # Two-step incremental package strategy (in chronological order,
701 # which is *not* the order in which the generated script has
702 # things):
703 #
704 # if stage is not "2/3" or "3/3":
705 # do verification on current system
706 # write recovery image to boot partition
707 # set stage to "2/3"
708 # reboot to boot partition and restart recovery
709 # else if stage is "2/3":
710 # write recovery image to recovery partition
711 # set stage to "3/3"
712 # reboot to recovery partition and restart recovery
713 # else:
714 # (stage must be "3/3")
715 # perform update:
716 # patch system files, etc.
717 # force full install of new boot image
718 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700719 # complete script normally
720 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800721
722 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700723 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800724 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700725 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800726 assert fs.fs_type.upper() == "EMMC", \
727 "two-step packages only supported on devices with EMMC /misc partitions"
728 bcb_dev = {"bcb_dev": fs.device}
729 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
730 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700731if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800732""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800733
734 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
735 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700736 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800737 script.WriteRawImage("/recovery", "recovery.img")
738 script.AppendExtra("""
739set_stage("%(bcb_dev)s", "3/3");
740reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700741else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800742""" % bcb_dev)
743
Tao Baod42e97e2016-11-30 12:11:57 -0800744 # Stage 1/3: (a) Verify the current system.
745 script.Comment("Stage 1/3")
746
Tao Bao6c55a8a2015-04-08 15:30:27 -0700747 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800748 script.Print("Source: %s" % (source_fp,))
749 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700750
Geremy Condra36bd3652014-02-06 19:45:10 -0800751 script.Print("Verifying current system...")
752
753 device_specific.IncrementalOTA_VerifyBegin()
754
Tao Bao3e30d972016-03-15 13:20:19 -0700755 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
756 # patching on a device that's already on the target build will damage the
757 # system. Because operations like move don't check the block state, they
758 # always apply the changes unconditionally.
759 if blockimgdiff_version <= 2:
760 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700761 script.AssertSomeFingerprint(source_fp)
762 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700763 script.AssertSomeThumbprint(
764 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700765
766 else: # blockimgdiff_version > 2
767 if source_oem_props is None and target_oem_props is None:
768 script.AssertSomeFingerprint(source_fp, target_fp)
769 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700770 script.AssertSomeThumbprint(
771 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
772 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700773 elif source_oem_props is None and target_oem_props is not None:
774 script.AssertFingerprintOrThumbprint(
775 source_fp,
776 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
777 else:
778 script.AssertFingerprintOrThumbprint(
779 target_fp,
780 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800781
Tao Baod8d14be2016-02-04 14:26:02 -0800782 # Check the required cache size (i.e. stashed blocks).
783 size = []
784 if system_diff:
785 size.append(system_diff.required_cache)
786 if vendor_diff:
787 size.append(vendor_diff.required_cache)
788
Geremy Condra36bd3652014-02-06 19:45:10 -0800789 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700790 boot_type, boot_device = common.GetTypeAndDevice(
791 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800792 d = common.Difference(target_boot, source_boot)
793 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700794 if d is None:
795 include_full_boot = True
796 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
797 else:
798 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800799
Tao Bao89fbb0f2017-01-10 10:47:58 -0800800 print("boot target: %d source: %d diff: %d" % (
801 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800802
Doug Zongkerf8340082014-08-05 10:39:37 -0700803 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800804
Doug Zongkerf8340082014-08-05 10:39:37 -0700805 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
806 (boot_type, boot_device,
807 source_boot.size, source_boot.sha1,
808 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800809 size.append(target_boot.size)
810
811 if size:
812 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800813
814 device_specific.IncrementalOTA_VerifyEnd()
815
816 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800817 # Stage 1/3: (b) Write recovery image to /boot.
818 _WriteRecoveryImageToBoot(script, output_zip)
819
Geremy Condra36bd3652014-02-06 19:45:10 -0800820 script.AppendExtra("""
821set_stage("%(bcb_dev)s", "2/3");
822reboot_now("%(bcb_dev)s", "");
823else
824""" % bcb_dev)
825
Tao Baod42e97e2016-11-30 12:11:57 -0800826 # Stage 3/3: Make changes.
827 script.Comment("Stage 3/3")
828
Jesse Zhao75bcea02015-01-06 10:59:53 -0800829 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700830 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800831 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700832 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800833
Geremy Condra36bd3652014-02-06 19:45:10 -0800834 script.Comment("---- start making changes here ----")
835
836 device_specific.IncrementalOTA_InstallBegin()
837
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700838 system_diff.WriteScript(script, output_zip,
839 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700840
Doug Zongkerfc44a512014-08-26 13:10:25 -0700841 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700842 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800843
844 if OPTIONS.two_step:
845 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
846 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800847 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800848
849 if not OPTIONS.two_step:
850 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700851 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800852 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700853 script.Print("Installing boot image...")
854 script.WriteRawImage("/boot", "boot.img")
855 else:
856 # Produce the boot image by applying a patch to the current
857 # contents of the boot partition, and write it back to the
858 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -0800859 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700860 script.Print("Patching boot image...")
861 script.ShowProgress(0.1, 10)
862 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
863 % (boot_type, boot_device,
864 source_boot.size, source_boot.sha1,
865 target_boot.size, target_boot.sha1),
866 "-",
867 target_boot.size, target_boot.sha1,
868 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800869 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800870 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800871
872 # Do device-specific installation (eg, write radio image).
873 device_specific.IncrementalOTA_InstallEnd()
874
875 if OPTIONS.extra_script is not None:
876 script.AppendExtra(OPTIONS.extra_script)
877
Doug Zongker922206e2014-03-04 13:16:24 -0800878 if OPTIONS.wipe_user_data:
879 script.Print("Erasing user data...")
880 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -0800881 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -0800882
Geremy Condra36bd3652014-02-06 19:45:10 -0800883 if OPTIONS.two_step:
884 script.AppendExtra("""
885set_stage("%(bcb_dev)s", "");
886endif;
887endif;
888""" % bcb_dev)
889
890 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -0800891 # For downgrade OTAs, we prefer to use the update-binary in the source
892 # build that is actually newer than the one in the target build.
893 if OPTIONS.downgrade:
894 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
895 else:
896 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800897 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -0800898 WriteMetadata(metadata, output_zip)
899
Doug Zongker32b527d2014-03-04 10:03:02 -0800900
Tao Bao9bc6bb22015-11-09 16:58:28 -0800901def WriteVerifyPackage(input_zip, output_zip):
902 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
903
904 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
905 recovery_mount_options = OPTIONS.info_dict.get(
906 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800907 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700908 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800909 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800910
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800911 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
912 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800913 metadata = {
914 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800915 "pre-device": GetOemProperty("ro.product.device", oem_props,
916 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -0800917 OPTIONS.info_dict),
918 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
919 }
920
921 device_specific = common.DeviceSpecificParams(
922 input_zip=input_zip,
923 input_version=OPTIONS.info_dict["recovery_api_version"],
924 output_zip=output_zip,
925 script=script,
926 input_tmp=OPTIONS.input_tmp,
927 metadata=metadata,
928 info_dict=OPTIONS.info_dict)
929
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800930 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800931
932 script.Print("Verifying device images against %s..." % target_fp)
933 script.AppendExtra("")
934
935 script.Print("Verifying boot...")
936 boot_img = common.GetBootableImage(
937 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
938 boot_type, boot_device = common.GetTypeAndDevice(
939 "/boot", OPTIONS.info_dict)
940 script.Verify("%s:%s:%d:%s" % (
941 boot_type, boot_device, boot_img.size, boot_img.sha1))
942 script.AppendExtra("")
943
944 script.Print("Verifying recovery...")
945 recovery_img = common.GetBootableImage(
946 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
947 recovery_type, recovery_device = common.GetTypeAndDevice(
948 "/recovery", OPTIONS.info_dict)
949 script.Verify("%s:%s:%d:%s" % (
950 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
951 script.AppendExtra("")
952
Tao Bao7e0f1602017-03-06 15:50:08 -0800953 system_tgt = GetImage("system", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800954 system_tgt.ResetFileMap()
955 system_diff = common.BlockDifference("system", system_tgt, src=None)
956 system_diff.WriteStrictVerifyScript(script)
957
958 if HasVendorPartition(input_zip):
Tao Bao7e0f1602017-03-06 15:50:08 -0800959 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800960 vendor_tgt.ResetFileMap()
961 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
962 vendor_diff.WriteStrictVerifyScript(script)
963
964 # Device specific partitions, such as radio, bootloader and etc.
965 device_specific.VerifyOTA_Assertions()
966
967 script.SetProgress(1.0)
968 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800969 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800970 WriteMetadata(metadata, output_zip)
971
972
Tao Baoc098e9e2016-01-07 13:03:56 -0800973def WriteABOTAPackageWithBrilloScript(target_file, output_file,
974 source_file=None):
975 """Generate an Android OTA package that has A/B update payload."""
976
Tao Bao2dd1c482017-02-03 16:49:39 -0800977 def ComputeStreamingMetadata(zip_file, reserve_space=False,
978 expected_length=None):
979 """Compute the streaming metadata for a given zip.
980
981 When 'reserve_space' is True, we reserve extra space for the offset and
982 length of the metadata entry itself, although we don't know the final
983 values until the package gets signed. This function will be called again
984 after signing. We then write the actual values and pad the string to the
985 length we set earlier. Note that we can't use the actual length of the
986 metadata entry in the second run. Otherwise the offsets for other entries
987 will be changing again.
988 """
Tao Baoc96316c2017-01-24 22:10:49 -0800989
990 def ComputeEntryOffsetSize(name):
991 """Compute the zip entry offset and size."""
992 info = zip_file.getinfo(name)
993 offset = info.header_offset + len(info.FileHeader())
994 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -0800995 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -0800996
997 # payload.bin and payload_properties.txt must exist.
998 offsets = [ComputeEntryOffsetSize('payload.bin'),
999 ComputeEntryOffsetSize('payload_properties.txt')]
1000
1001 # care_map.txt is available only if dm-verity is enabled.
1002 if 'care_map.txt' in zip_file.namelist():
1003 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001004
Michael Schwartz853cd302017-04-10 13:27:19 -07001005 if 'compatibility.zip' in zip_file.namelist():
1006 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1007
Tao Bao2dd1c482017-02-03 16:49:39 -08001008 # 'META-INF/com/android/metadata' is required. We don't know its actual
1009 # offset and length (as well as the values for other entries). So we
1010 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1011 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1012 # beginning of the zip), as well as the possible value changes in other
1013 # entries.
1014 if reserve_space:
1015 offsets.append('metadata:' + ' ' * 10)
1016 else:
1017 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1018
1019 value = ','.join(offsets)
1020 if expected_length is not None:
1021 assert len(value) <= expected_length, \
1022 'Insufficient reserved space: reserved=%d, actual=%d' % (
1023 expected_length, len(value))
1024 value += ' ' * (expected_length - len(value))
1025 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001026
Alex Deymod8d96ec2016-06-10 16:38:31 -07001027 # The place where the output from the subprocess should go.
1028 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1029
Tao Baoc098e9e2016-01-07 13:03:56 -08001030 # Setup signing keys.
1031 if OPTIONS.package_key is None:
1032 OPTIONS.package_key = OPTIONS.info_dict.get(
1033 "default_system_dev_certificate",
1034 "build/target/product/security/testkey")
1035
Tao Baodea0f8b2016-06-20 17:55:06 -07001036 # A/B updater expects a signing key in RSA format. Gets the key ready for
1037 # later use in step 3, unless a payload_signer has been specified.
1038 if OPTIONS.payload_signer is None:
1039 cmd = ["openssl", "pkcs8",
1040 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1041 "-inform", "DER", "-nocrypt"]
1042 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1043 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001044 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1045 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001046 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001047
Tao Baodea0f8b2016-06-20 17:55:06 -07001048 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001049 temp_zip_file = tempfile.NamedTemporaryFile()
1050 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1051 compression=zipfile.ZIP_DEFLATED)
1052
1053 # Metadata to comply with Android OTA package format.
1054 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001055 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001056 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -08001057 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001058
1059 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001060 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001061 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001062 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1063 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001064 "pre-device": GetOemProperty("ro.product.device", oem_props,
1065 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001066 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001067 "ota-required-cache": "0",
1068 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001069 }
1070
1071 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001072 metadata["pre-build"] = CalculateFingerprint(oem_props,
1073 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001074 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001075 metadata["pre-build-incremental"] = GetBuildProp(
1076 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001077
Tao Baob31892e2017-02-07 11:21:17 -08001078 HandleDowngradeMetadata(metadata)
1079 else:
1080 metadata["post-timestamp"] = GetBuildProp(
1081 "ro.build.date.utc", OPTIONS.info_dict)
1082
Tao Baoc098e9e2016-01-07 13:03:56 -08001083 # 1. Generate payload.
1084 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1085 cmd = ["brillo_update_payload", "generate",
1086 "--payload", payload_file,
1087 "--target_image", target_file]
1088 if source_file is not None:
1089 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001090 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1091 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001092 assert p1.returncode == 0, "brillo_update_payload generate failed"
1093
1094 # 2. Generate hashes of the payload and metadata files.
1095 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1096 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1097 cmd = ["brillo_update_payload", "hash",
1098 "--unsigned_payload", payload_file,
1099 "--signature_size", "256",
1100 "--metadata_hash_file", metadata_sig_file,
1101 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001102 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1103 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001104 assert p1.returncode == 0, "brillo_update_payload hash failed"
1105
1106 # 3. Sign the hashes and insert them back into the payload file.
1107 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1108 suffix=".bin")
1109 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1110 suffix=".bin")
1111 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001112 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001113 cmd = [OPTIONS.payload_signer]
1114 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001115 else:
1116 cmd = ["openssl", "pkeyutl", "-sign",
1117 "-inkey", rsa_key,
1118 "-pkeyopt", "digest:sha256"]
1119 cmd.extend(["-in", payload_sig_file,
1120 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001121 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1122 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001123 assert p1.returncode == 0, "openssl sign payload failed"
1124
1125 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001126 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001127 cmd = [OPTIONS.payload_signer]
1128 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001129 else:
1130 cmd = ["openssl", "pkeyutl", "-sign",
1131 "-inkey", rsa_key,
1132 "-pkeyopt", "digest:sha256"]
1133 cmd.extend(["-in", metadata_sig_file,
1134 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001135 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1136 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001137 assert p1.returncode == 0, "openssl sign metadata failed"
1138
1139 # 3c. Insert the signatures back into the payload file.
1140 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1141 suffix=".bin")
1142 cmd = ["brillo_update_payload", "sign",
1143 "--unsigned_payload", payload_file,
1144 "--payload", signed_payload_file,
1145 "--signature_size", "256",
1146 "--metadata_signature_file", signed_metadata_sig_file,
1147 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001148 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1149 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001150 assert p1.returncode == 0, "brillo_update_payload sign failed"
1151
Alex Deymo19241c12016-02-04 22:29:29 -08001152 # 4. Dump the signed payload properties.
1153 properties_file = common.MakeTempFile(prefix="payload-properties-",
1154 suffix=".txt")
1155 cmd = ["brillo_update_payload", "properties",
1156 "--payload", signed_payload_file,
1157 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001158 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1159 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001160 assert p1.returncode == 0, "brillo_update_payload properties failed"
1161
Tao Bao7c5dc572016-06-14 17:48:11 -07001162 if OPTIONS.wipe_user_data:
1163 with open(properties_file, "a") as f:
1164 f.write("POWERWASH=1\n")
1165 metadata["ota-wipe"] = "yes"
1166
Tao Baoc96316c2017-01-24 22:10:49 -08001167 # Add the signed payload file and properties into the zip. In order to
1168 # support streaming, we pack payload.bin, payload_properties.txt and
1169 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1170 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001171 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1172 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001173 common.ZipWrite(output_zip, properties_file,
1174 arcname="payload_properties.txt",
1175 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001176
Tianjie Xucfa86222016-03-07 16:31:19 -08001177 # If dm-verity is supported for the device, copy contents of care_map
1178 # into A/B OTA package.
Michael Schwartz853cd302017-04-10 13:27:19 -07001179 target_zip = zipfile.ZipFile(target_file, "r")
Tianjie Xucfa86222016-03-07 16:31:19 -08001180 if OPTIONS.info_dict.get("verity") == "true":
Tianjie Xucfa86222016-03-07 16:31:19 -08001181 care_map_path = "META/care_map.txt"
1182 namelist = target_zip.namelist()
1183 if care_map_path in namelist:
1184 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001185 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1186 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001187 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001188 print("Warning: cannot find care map file in target_file package")
Michael Schwartz853cd302017-04-10 13:27:19 -07001189
1190 if HasVendorPartition(target_zip):
1191 update_vendor = True
1192 update_system = True
1193
1194 # If incremental then figure out what is being updated so metadata only for
1195 # the updated image is included.
1196 if source_file is not None:
1197 input_tmp, input_zip = common.UnzipTemp(
1198 target_file, UNZIP_PATTERN)
1199 source_tmp, source_zip = common.UnzipTemp(
1200 source_file, UNZIP_PATTERN)
1201
1202 vendor_src = GetImage("vendor", source_tmp)
1203 vendor_tgt = GetImage("vendor", input_tmp)
1204 system_src = GetImage("system", source_tmp)
1205 system_tgt = GetImage("system", input_tmp)
1206
1207 update_system = system_src.TotalSha1() != system_tgt.TotalSha1()
1208 update_vendor = vendor_src.TotalSha1() != vendor_tgt.TotalSha1()
1209
1210 input_zip.close()
1211 source_zip.close()
1212
1213 target_zip = zipfile.ZipFile(target_file, "r")
1214 AddCompatibilityArchive(target_zip, output_zip, update_system,
1215 update_vendor)
1216 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001217
Tao Bao2dd1c482017-02-03 16:49:39 -08001218 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001219 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001220 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001221 WriteMetadata(metadata, output_zip)
1222 common.ZipClose(output_zip)
1223
Tao Bao2dd1c482017-02-03 16:49:39 -08001224 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1225 # zip entries, as well as padding the entry headers. We do a preliminary
1226 # signing (with an incomplete metadata entry) to allow that to happen. Then
1227 # compute the zip entry offsets, write back the final metadata and do the
1228 # final signing.
1229 prelim_signing = tempfile.NamedTemporaryFile()
1230 SignOutput(temp_zip_file.name, prelim_signing.name)
1231 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001232
Tao Bao2dd1c482017-02-03 16:49:39 -08001233 # Open the signed zip. Compute the final metadata that's needed for streaming.
1234 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1235 compression=zipfile.ZIP_DEFLATED)
1236 expected_length = len(metadata['ota-streaming-property-files'])
1237 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1238 prelim_zip, reserve_space=False, expected_length=expected_length)
1239
1240 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1241 final_signing = tempfile.NamedTemporaryFile()
1242 output_zip = zipfile.ZipFile(final_signing, "w",
1243 compression=zipfile.ZIP_DEFLATED)
1244 for item in prelim_zip.infolist():
1245 if item.filename == METADATA_NAME:
1246 continue
1247
1248 data = prelim_zip.read(item.filename)
1249 out_info = copy.copy(item)
1250 common.ZipWriteStr(output_zip, out_info, data)
1251
1252 # Now write the final metadata entry.
1253 WriteMetadata(metadata, output_zip)
1254 common.ZipClose(prelim_zip)
1255 common.ZipClose(output_zip)
1256
1257 # Re-sign the package after updating the metadata entry.
1258 SignOutput(final_signing.name, output_file)
1259 final_signing.close()
1260
1261 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001262 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001263 actual = metadata['ota-streaming-property-files'].strip()
1264 expected = ComputeStreamingMetadata(output_zip)
1265 assert actual == expected, \
1266 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001267 common.ZipClose(output_zip)
1268
Tao Baoc098e9e2016-01-07 13:03:56 -08001269
Doug Zongkereef39442009-04-02 12:14:19 -07001270def main(argv):
1271
1272 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001273 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001274 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001275 elif o in ("-k", "--package_key"):
1276 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001277 elif o in ("-i", "--incremental_from"):
1278 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001279 elif o == "--full_radio":
1280 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001281 elif o == "--full_bootloader":
1282 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001283 elif o in ("-w", "--wipe_user_data"):
1284 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001285 elif o == "--downgrade":
1286 OPTIONS.downgrade = True
1287 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001288 elif o == "--override_timestamp":
1289 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001290 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001291 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001292 elif o == "--oem_no_mount":
1293 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001294 elif o in ("-e", "--extra_script"):
1295 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001296 elif o in ("-t", "--worker_threads"):
1297 if a.isdigit():
1298 OPTIONS.worker_threads = int(a)
1299 else:
1300 raise ValueError("Cannot parse value %r for option %r - only "
1301 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001302 elif o in ("-2", "--two_step"):
1303 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001304 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001305 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001306 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001307 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001308 elif o == "--block":
1309 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001310 elif o in ("-b", "--binary"):
1311 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001312 elif o in ("--no_fallback_to_full",):
1313 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001314 elif o == "--stash_threshold":
1315 try:
1316 OPTIONS.stash_threshold = float(a)
1317 except ValueError:
1318 raise ValueError("Cannot parse value %r for option %r - expecting "
1319 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001320 elif o == "--gen_verify":
1321 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001322 elif o == "--log_diff":
1323 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001324 elif o == "--payload_signer":
1325 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001326 elif o == "--payload_signer_args":
1327 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001328 elif o == "--extracted_input_target_files":
1329 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001330 else:
1331 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001332 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001333
1334 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08001335 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001336 extra_long_opts=[
1337 "board_config=",
1338 "package_key=",
1339 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001340 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001341 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001342 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001343 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001344 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001345 "extra_script=",
1346 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001347 "two_step",
1348 "no_signing",
1349 "block",
1350 "binary=",
1351 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001352 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001353 "verify",
1354 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001355 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001356 "gen_verify",
1357 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001358 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001359 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001360 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001361 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001362
1363 if len(args) != 2:
1364 common.Usage(__doc__)
1365 sys.exit(1)
1366
Tao Bao5d182562016-02-23 11:38:39 -08001367 if OPTIONS.downgrade:
1368 # Sanity check to enforce a data wipe.
1369 if not OPTIONS.wipe_user_data:
1370 raise ValueError("Cannot downgrade without a data wipe")
1371
1372 # We should only allow downgrading incrementals (as opposed to full).
1373 # Otherwise the device may go back from arbitrary build with this full
1374 # OTA package.
1375 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001376 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001377
Tao Bao3e6161a2017-02-28 11:48:48 -08001378 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1379 "Cannot have --downgrade AND --override_timestamp both"
1380
Tao Baoc098e9e2016-01-07 13:03:56 -08001381 # Load the dict file from the zip directly to have a peek at the OTA type.
1382 # For packages using A/B update, unzipping is not needed.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001383 if OPTIONS.extracted_input is not None:
1384 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input)
1385 else:
1386 input_zip = zipfile.ZipFile(args[0], "r")
1387 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1388 common.ZipClose(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001389
1390 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1391
1392 if ab_update:
1393 if OPTIONS.incremental_source is not None:
1394 OPTIONS.target_info_dict = OPTIONS.info_dict
1395 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1396 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1397 common.ZipClose(source_zip)
1398
1399 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001400 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001401 common.DumpInfoDict(OPTIONS.info_dict)
1402
1403 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001404 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001405 common.DumpInfoDict(OPTIONS.source_info_dict)
1406
1407 WriteABOTAPackageWithBrilloScript(
1408 target_file=args[0],
1409 output_file=args[1],
1410 source_file=OPTIONS.incremental_source)
1411
Tao Bao89fbb0f2017-01-10 10:47:58 -08001412 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001413 return
1414
Doug Zongker1c390a22009-05-14 19:06:36 -07001415 if OPTIONS.extra_script is not None:
1416 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1417
Dan Willemsencea5cd22017-03-21 14:44:27 -07001418 if OPTIONS.extracted_input is not None:
1419 OPTIONS.input_tmp = OPTIONS.extracted_input
1420 OPTIONS.target_tmp = OPTIONS.input_tmp
1421 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp)
1422 input_zip = zipfile.ZipFile(args[0], "r")
1423 else:
1424 print("unzipping target target-files...")
1425 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1426 args[0], UNZIP_PATTERN)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001427
Dan Willemsencea5cd22017-03-21 14:44:27 -07001428 OPTIONS.target_tmp = OPTIONS.input_tmp
1429 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001430
Doug Zongker37974732010-09-16 17:44:38 -07001431 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001432 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07001433 common.DumpInfoDict(OPTIONS.info_dict)
1434
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001435 # If the caller explicitly specified the device-specific extensions
1436 # path via -s/--device_specific, use that. Otherwise, use
1437 # META/releasetools.py if it is present in the target target_files.
1438 # Otherwise, take the path of the file from 'tool_extensions' in the
1439 # info dict and look for that in the local filesystem, relative to
1440 # the current directory.
1441
Doug Zongker37974732010-09-16 17:44:38 -07001442 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001443 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1444 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001445 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001446 OPTIONS.device_specific = from_input
1447 else:
1448 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1449
Doug Zongker37974732010-09-16 17:44:38 -07001450 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001451 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001452
Tao Baoc098e9e2016-01-07 13:03:56 -08001453 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001454 raise common.ExternalError(
1455 "--- target build has specified no recovery ---")
1456
Tao Bao767e3ac2015-11-10 12:19:19 -08001457 # Use the default key to sign the package if not specified with package_key.
1458 if not OPTIONS.no_signing:
1459 if OPTIONS.package_key is None:
1460 OPTIONS.package_key = OPTIONS.info_dict.get(
1461 "default_system_dev_certificate",
1462 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001463
Tao Bao767e3ac2015-11-10 12:19:19 -08001464 # Set up the output zip. Create a temporary zip file if signing is needed.
1465 if OPTIONS.no_signing:
1466 if os.path.exists(args[1]):
1467 os.unlink(args[1])
1468 output_zip = zipfile.ZipFile(args[1], "w",
1469 compression=zipfile.ZIP_DEFLATED)
1470 else:
1471 temp_zip_file = tempfile.NamedTemporaryFile()
1472 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1473 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001474
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001475 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001476 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001477 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001478 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001479 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001480
Tao Bao9bc6bb22015-11-09 16:58:28 -08001481 # Generate a verify package.
1482 if OPTIONS.gen_verify:
1483 WriteVerifyPackage(input_zip, output_zip)
1484
Tao Bao767e3ac2015-11-10 12:19:19 -08001485 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001486 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001487 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001488
1489 # Generate an incremental OTA. It will fall back to generate a full OTA on
1490 # failure unless no_fallback_to_full is specified.
1491 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001492 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001493 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001494 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001495 UNZIP_PATTERN)
Tao Bao767e3ac2015-11-10 12:19:19 -08001496 OPTIONS.target_info_dict = OPTIONS.info_dict
1497 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1498 OPTIONS.source_tmp)
1499 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001500 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001501 common.DumpInfoDict(OPTIONS.source_info_dict)
1502 try:
Tao Bao457cbf62017-03-06 09:56:01 -08001503 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001504 if OPTIONS.log_diff:
1505 out_file = open(OPTIONS.log_diff, 'w')
1506 import target_files_diff
1507 target_files_diff.recursiveDiff('',
1508 OPTIONS.source_tmp,
1509 OPTIONS.input_tmp,
1510 out_file)
1511 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001512 except ValueError:
1513 if not OPTIONS.fallback_to_full:
1514 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08001515 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001516 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001517 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001518
Tao Bao767e3ac2015-11-10 12:19:19 -08001519 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001520
Tao Bao767e3ac2015-11-10 12:19:19 -08001521 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001522 if not OPTIONS.no_signing:
1523 SignOutput(temp_zip_file.name, args[1])
1524 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001525
Tao Bao89fbb0f2017-01-10 10:47:58 -08001526 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001527
1528
1529if __name__ == '__main__':
1530 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001531 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001532 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001533 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001534 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001535 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001536 finally:
1537 common.Cleanup()