blob: 20904001dbfcd546e51bd730ff21c56be2b51cd9 [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
Christian Oderf63e2cd2017-05-01 22:30:15 +0200184OPTIONS.key_passwords = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700185
Tao Bao2dd1c482017-02-03 16:49:39 -0800186METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800187UNZIP_PATTERN = ['IMAGES/*', 'META/*']
188
Tao Bao2dd1c482017-02-03 16:49:39 -0800189
Doug Zongkereef39442009-04-02 12:14:19 -0700190def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200191 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700192
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
Tao Bao21803d32017-04-19 10:16:09 -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
Doug Zongker37974732010-09-16 17:44:38 -0700502 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700503 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700504
Doug Zongker01ce19c2014-02-04 13:48:15 -0800505 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700506 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700507
Doug Zongker01ce19c2014-02-04 13:48:15 -0800508 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700509 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700510
Doug Zongker1c390a22009-05-14 19:06:36 -0700511 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700512 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700513
Doug Zongker14833602010-02-02 13:12:04 -0800514 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800515
Doug Zongker922206e2014-03-04 13:16:24 -0800516 if OPTIONS.wipe_user_data:
517 script.ShowProgress(0.1, 10)
518 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700519
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800520 if OPTIONS.two_step:
521 script.AppendExtra("""
522set_stage("%(bcb_dev)s", "");
523""" % bcb_dev)
524 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800525
526 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
527 script.Comment("Stage 1/3")
528 _WriteRecoveryImageToBoot(script, output_zip)
529
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800530 script.AppendExtra("""
531set_stage("%(bcb_dev)s", "2/3");
532reboot_now("%(bcb_dev)s", "");
533endif;
534endif;
535""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800536
Tao Bao5d182562016-02-23 11:38:39 -0800537 script.SetProgress(1)
538 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800539 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700540 WriteMetadata(metadata, output_zip)
541
Doug Zongkerfc44a512014-08-26 13:10:25 -0700542
Dan Albert8e0178d2015-01-27 15:53:15 -0800543def WritePolicyConfig(file_name, output_zip):
544 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500545
Doug Zongker2ea21062010-04-28 16:05:21 -0700546
547def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800548 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
549 common.ZipWriteStr(output_zip, METADATA_NAME, value,
550 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700551
Doug Zongkerfc44a512014-08-26 13:10:25 -0700552
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700553def GetBuildProp(prop, info_dict):
554 """Return the fingerprint of the build of a given target-files info_dict."""
555 try:
556 return info_dict.get("build.prop", {})[prop]
557 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700558 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700559
Doug Zongkerfc44a512014-08-26 13:10:25 -0700560
Tao Baob31892e2017-02-07 11:21:17 -0800561def HandleDowngradeMetadata(metadata):
562 # Only incremental OTAs are allowed to reach here.
563 assert OPTIONS.incremental_source is not None
564
565 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
566 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
567 is_downgrade = long(post_timestamp) < long(pre_timestamp)
568
569 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800570 if not is_downgrade:
571 raise RuntimeError("--downgrade specified but no downgrade detected: "
572 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800573 metadata["ota-downgrade"] = "yes"
574 elif OPTIONS.timestamp:
575 if not is_downgrade:
576 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
577 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
578 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800579 else:
580 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800581 raise RuntimeError("Downgrade detected based on timestamp check: "
582 "pre: %s, post: %s. Need to specify --timestamp OR "
583 "--downgrade to allow building the incremental." % (
584 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800585 metadata["post-timestamp"] = post_timestamp
586
587
Geremy Condra36bd3652014-02-06 19:45:10 -0800588def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
589 source_version = OPTIONS.source_info_dict["recovery_api_version"]
590 target_version = OPTIONS.target_info_dict["recovery_api_version"]
591
592 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700593 print("WARNING: generating edify script for a source that "
594 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700595 script = edify_generator.EdifyGenerator(
596 source_version, OPTIONS.target_info_dict,
597 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800598
Tao Bao3806c232015-07-05 21:08:33 -0700599 recovery_mount_options = OPTIONS.source_info_dict.get(
600 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700601 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
602 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800603 oem_dicts = None
604 if source_oem_props and target_oem_props:
605 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700606
Dan Albert8b72aef2015-03-23 19:13:21 -0700607 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700608 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800609 oem_dicts and oem_dicts[0],
610 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800611 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700612 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800613
Tao Baob31892e2017-02-07 11:21:17 -0800614 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800615
Geremy Condra36bd3652014-02-06 19:45:10 -0800616 device_specific = common.DeviceSpecificParams(
617 source_zip=source_zip,
618 source_version=source_version,
619 target_zip=target_zip,
620 target_version=target_version,
621 output_zip=output_zip,
622 script=script,
623 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700624 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800625
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800626 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700627 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800628 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700629 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800630 metadata["pre-build"] = source_fp
631 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700632 metadata["pre-build-incremental"] = GetBuildProp(
633 "ro.build.version.incremental", OPTIONS.source_info_dict)
634 metadata["post-build-incremental"] = GetBuildProp(
635 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800636
637 source_boot = common.GetBootableImage(
638 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
639 OPTIONS.source_info_dict)
640 target_boot = common.GetBootableImage(
641 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
642 updating_boot = (not OPTIONS.two_step and
643 (source_boot.data != target_boot.data))
644
Geremy Condra36bd3652014-02-06 19:45:10 -0800645 target_recovery = common.GetBootableImage(
646 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800647
Tao Bao7e0f1602017-03-06 15:50:08 -0800648 system_src = GetImage("system", OPTIONS.source_tmp)
649 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700650
651 blockimgdiff_version = 1
652 if OPTIONS.info_dict:
653 blockimgdiff_version = max(
654 int(i) for i in
655 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
656
Tao Baof8acad12016-07-07 09:09:58 -0700657 # Check the first block of the source system partition for remount R/W only
658 # if the filesystem is ext4.
659 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
660 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700661 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
662 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
663 # b) the blocks listed in block map may not contain all the bytes for a given
664 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700665 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
666 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
667 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700668 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800669 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700670 version=blockimgdiff_version,
671 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700672
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700673 if HasVendorPartition(target_zip):
674 if not HasVendorPartition(source_zip):
675 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800676 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
677 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800678
679 # Check first block of vendor partition for remount R/W only if
680 # disk type is ext4
681 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800682 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700683 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700684 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800685 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700686 version=blockimgdiff_version,
687 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700688 else:
689 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800690
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800691 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800692 device_specific.IncrementalOTA_Assertions()
693
694 # Two-step incremental package strategy (in chronological order,
695 # which is *not* the order in which the generated script has
696 # things):
697 #
698 # if stage is not "2/3" or "3/3":
699 # do verification on current system
700 # write recovery image to boot partition
701 # set stage to "2/3"
702 # reboot to boot partition and restart recovery
703 # else if stage is "2/3":
704 # write recovery image to recovery partition
705 # set stage to "3/3"
706 # reboot to recovery partition and restart recovery
707 # else:
708 # (stage must be "3/3")
709 # perform update:
710 # patch system files, etc.
711 # force full install of new boot image
712 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700713 # complete script normally
714 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800715
716 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700717 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800718 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700719 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800720 assert fs.fs_type.upper() == "EMMC", \
721 "two-step packages only supported on devices with EMMC /misc partitions"
722 bcb_dev = {"bcb_dev": fs.device}
723 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
724 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700725if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800726""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800727
728 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
729 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700730 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800731 script.WriteRawImage("/recovery", "recovery.img")
732 script.AppendExtra("""
733set_stage("%(bcb_dev)s", "3/3");
734reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700735else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800736""" % bcb_dev)
737
Tao Baod42e97e2016-11-30 12:11:57 -0800738 # Stage 1/3: (a) Verify the current system.
739 script.Comment("Stage 1/3")
740
Tao Bao6c55a8a2015-04-08 15:30:27 -0700741 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800742 script.Print("Source: %s" % (source_fp,))
743 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700744
Geremy Condra36bd3652014-02-06 19:45:10 -0800745 script.Print("Verifying current system...")
746
747 device_specific.IncrementalOTA_VerifyBegin()
748
Tao Bao3e30d972016-03-15 13:20:19 -0700749 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
750 # patching on a device that's already on the target build will damage the
751 # system. Because operations like move don't check the block state, they
752 # always apply the changes unconditionally.
753 if blockimgdiff_version <= 2:
754 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700755 script.AssertSomeFingerprint(source_fp)
756 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700757 script.AssertSomeThumbprint(
758 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700759
760 else: # blockimgdiff_version > 2
761 if source_oem_props is None and target_oem_props is None:
762 script.AssertSomeFingerprint(source_fp, target_fp)
763 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700764 script.AssertSomeThumbprint(
765 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
766 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700767 elif source_oem_props is None and target_oem_props is not None:
768 script.AssertFingerprintOrThumbprint(
769 source_fp,
770 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
771 else:
772 script.AssertFingerprintOrThumbprint(
773 target_fp,
774 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800775
Tao Baod8d14be2016-02-04 14:26:02 -0800776 # Check the required cache size (i.e. stashed blocks).
777 size = []
778 if system_diff:
779 size.append(system_diff.required_cache)
780 if vendor_diff:
781 size.append(vendor_diff.required_cache)
782
Geremy Condra36bd3652014-02-06 19:45:10 -0800783 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700784 boot_type, boot_device = common.GetTypeAndDevice(
785 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800786 d = common.Difference(target_boot, source_boot)
787 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700788 if d is None:
789 include_full_boot = True
790 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
791 else:
792 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800793
Tao Bao89fbb0f2017-01-10 10:47:58 -0800794 print("boot target: %d source: %d diff: %d" % (
795 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800796
Doug Zongkerf8340082014-08-05 10:39:37 -0700797 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800798
Doug Zongkerf8340082014-08-05 10:39:37 -0700799 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
800 (boot_type, boot_device,
801 source_boot.size, source_boot.sha1,
802 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800803 size.append(target_boot.size)
804
805 if size:
806 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800807
808 device_specific.IncrementalOTA_VerifyEnd()
809
810 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800811 # Stage 1/3: (b) Write recovery image to /boot.
812 _WriteRecoveryImageToBoot(script, output_zip)
813
Geremy Condra36bd3652014-02-06 19:45:10 -0800814 script.AppendExtra("""
815set_stage("%(bcb_dev)s", "2/3");
816reboot_now("%(bcb_dev)s", "");
817else
818""" % bcb_dev)
819
Tao Baod42e97e2016-11-30 12:11:57 -0800820 # Stage 3/3: Make changes.
821 script.Comment("Stage 3/3")
822
Jesse Zhao75bcea02015-01-06 10:59:53 -0800823 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700824 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800825 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700826 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800827
Geremy Condra36bd3652014-02-06 19:45:10 -0800828 script.Comment("---- start making changes here ----")
829
830 device_specific.IncrementalOTA_InstallBegin()
831
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700832 system_diff.WriteScript(script, output_zip,
833 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700834
Doug Zongkerfc44a512014-08-26 13:10:25 -0700835 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700836 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800837
838 if OPTIONS.two_step:
839 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
840 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800841 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800842
843 if not OPTIONS.two_step:
844 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700845 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800846 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700847 script.Print("Installing boot image...")
848 script.WriteRawImage("/boot", "boot.img")
849 else:
850 # Produce the boot image by applying a patch to the current
851 # contents of the boot partition, and write it back to the
852 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -0800853 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700854 script.Print("Patching boot image...")
855 script.ShowProgress(0.1, 10)
856 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
857 % (boot_type, boot_device,
858 source_boot.size, source_boot.sha1,
859 target_boot.size, target_boot.sha1),
860 "-",
861 target_boot.size, target_boot.sha1,
862 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800863 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800864 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800865
866 # Do device-specific installation (eg, write radio image).
867 device_specific.IncrementalOTA_InstallEnd()
868
869 if OPTIONS.extra_script is not None:
870 script.AppendExtra(OPTIONS.extra_script)
871
Doug Zongker922206e2014-03-04 13:16:24 -0800872 if OPTIONS.wipe_user_data:
873 script.Print("Erasing user data...")
874 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -0800875 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -0800876
Geremy Condra36bd3652014-02-06 19:45:10 -0800877 if OPTIONS.two_step:
878 script.AppendExtra("""
879set_stage("%(bcb_dev)s", "");
880endif;
881endif;
882""" % bcb_dev)
883
884 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -0800885 # For downgrade OTAs, we prefer to use the update-binary in the source
886 # build that is actually newer than the one in the target build.
887 if OPTIONS.downgrade:
888 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
889 else:
890 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800891 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -0800892 WriteMetadata(metadata, output_zip)
893
Doug Zongker32b527d2014-03-04 10:03:02 -0800894
Tao Bao9bc6bb22015-11-09 16:58:28 -0800895def WriteVerifyPackage(input_zip, output_zip):
896 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
897
898 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
899 recovery_mount_options = OPTIONS.info_dict.get(
900 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800901 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700902 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800903 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800904
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800905 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
906 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800907 metadata = {
908 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800909 "pre-device": GetOemProperty("ro.product.device", oem_props,
910 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -0800911 OPTIONS.info_dict),
912 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
913 }
914
915 device_specific = common.DeviceSpecificParams(
916 input_zip=input_zip,
917 input_version=OPTIONS.info_dict["recovery_api_version"],
918 output_zip=output_zip,
919 script=script,
920 input_tmp=OPTIONS.input_tmp,
921 metadata=metadata,
922 info_dict=OPTIONS.info_dict)
923
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800924 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800925
926 script.Print("Verifying device images against %s..." % target_fp)
927 script.AppendExtra("")
928
929 script.Print("Verifying boot...")
930 boot_img = common.GetBootableImage(
931 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
932 boot_type, boot_device = common.GetTypeAndDevice(
933 "/boot", OPTIONS.info_dict)
934 script.Verify("%s:%s:%d:%s" % (
935 boot_type, boot_device, boot_img.size, boot_img.sha1))
936 script.AppendExtra("")
937
938 script.Print("Verifying recovery...")
939 recovery_img = common.GetBootableImage(
940 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
941 recovery_type, recovery_device = common.GetTypeAndDevice(
942 "/recovery", OPTIONS.info_dict)
943 script.Verify("%s:%s:%d:%s" % (
944 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
945 script.AppendExtra("")
946
Tao Bao7e0f1602017-03-06 15:50:08 -0800947 system_tgt = GetImage("system", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800948 system_tgt.ResetFileMap()
949 system_diff = common.BlockDifference("system", system_tgt, src=None)
950 system_diff.WriteStrictVerifyScript(script)
951
952 if HasVendorPartition(input_zip):
Tao Bao7e0f1602017-03-06 15:50:08 -0800953 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800954 vendor_tgt.ResetFileMap()
955 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
956 vendor_diff.WriteStrictVerifyScript(script)
957
958 # Device specific partitions, such as radio, bootloader and etc.
959 device_specific.VerifyOTA_Assertions()
960
961 script.SetProgress(1.0)
962 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800963 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800964 WriteMetadata(metadata, output_zip)
965
966
Tao Baoc098e9e2016-01-07 13:03:56 -0800967def WriteABOTAPackageWithBrilloScript(target_file, output_file,
968 source_file=None):
969 """Generate an Android OTA package that has A/B update payload."""
970
Tao Bao2dd1c482017-02-03 16:49:39 -0800971 def ComputeStreamingMetadata(zip_file, reserve_space=False,
972 expected_length=None):
973 """Compute the streaming metadata for a given zip.
974
975 When 'reserve_space' is True, we reserve extra space for the offset and
976 length of the metadata entry itself, although we don't know the final
977 values until the package gets signed. This function will be called again
978 after signing. We then write the actual values and pad the string to the
979 length we set earlier. Note that we can't use the actual length of the
980 metadata entry in the second run. Otherwise the offsets for other entries
981 will be changing again.
982 """
Tao Baoc96316c2017-01-24 22:10:49 -0800983
984 def ComputeEntryOffsetSize(name):
985 """Compute the zip entry offset and size."""
986 info = zip_file.getinfo(name)
987 offset = info.header_offset + len(info.FileHeader())
988 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -0800989 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -0800990
991 # payload.bin and payload_properties.txt must exist.
992 offsets = [ComputeEntryOffsetSize('payload.bin'),
993 ComputeEntryOffsetSize('payload_properties.txt')]
994
995 # care_map.txt is available only if dm-verity is enabled.
996 if 'care_map.txt' in zip_file.namelist():
997 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -0800998
Tao Bao21803d32017-04-19 10:16:09 -0700999 if 'compatibility.zip' in zip_file.namelist():
1000 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1001
Tao Bao2dd1c482017-02-03 16:49:39 -08001002 # 'META-INF/com/android/metadata' is required. We don't know its actual
1003 # offset and length (as well as the values for other entries). So we
1004 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1005 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1006 # beginning of the zip), as well as the possible value changes in other
1007 # entries.
1008 if reserve_space:
1009 offsets.append('metadata:' + ' ' * 10)
1010 else:
1011 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1012
1013 value = ','.join(offsets)
1014 if expected_length is not None:
1015 assert len(value) <= expected_length, \
1016 'Insufficient reserved space: reserved=%d, actual=%d' % (
1017 expected_length, len(value))
1018 value += ' ' * (expected_length - len(value))
1019 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001020
Alex Deymod8d96ec2016-06-10 16:38:31 -07001021 # The place where the output from the subprocess should go.
1022 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1023
Tao Baodea0f8b2016-06-20 17:55:06 -07001024 # A/B updater expects a signing key in RSA format. Gets the key ready for
1025 # later use in step 3, unless a payload_signer has been specified.
1026 if OPTIONS.payload_signer is None:
1027 cmd = ["openssl", "pkcs8",
1028 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
Christian Oderf63e2cd2017-05-01 22:30:15 +02001029 "-inform", "DER"]
1030 pw = OPTIONS.key_passwords[OPTIONS.package_key]
1031 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
Tao Baodea0f8b2016-06-20 17:55:06 -07001032 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1033 cmd.extend(["-out", rsa_key])
Christian Oderf63e2cd2017-05-01 22:30:15 +02001034 p1 = common.Run(cmd, verbose=False, stdout=log_file, stderr=subprocess.STDOUT)
Tao Bao6047c242016-06-21 13:35:26 -07001035 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001036 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001037
Tao Baodea0f8b2016-06-20 17:55:06 -07001038 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001039 temp_zip_file = tempfile.NamedTemporaryFile()
1040 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1041 compression=zipfile.ZIP_DEFLATED)
1042
1043 # Metadata to comply with Android OTA package format.
1044 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001045 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001046 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -08001047 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001048
1049 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001050 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001051 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001052 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1053 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001054 "pre-device": GetOemProperty("ro.product.device", oem_props,
1055 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001056 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001057 "ota-required-cache": "0",
1058 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001059 }
1060
1061 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001062 metadata["pre-build"] = CalculateFingerprint(oem_props,
1063 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001064 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001065 metadata["pre-build-incremental"] = GetBuildProp(
1066 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001067
Tao Baob31892e2017-02-07 11:21:17 -08001068 HandleDowngradeMetadata(metadata)
1069 else:
1070 metadata["post-timestamp"] = GetBuildProp(
1071 "ro.build.date.utc", OPTIONS.info_dict)
1072
Tao Baoc098e9e2016-01-07 13:03:56 -08001073 # 1. Generate payload.
1074 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1075 cmd = ["brillo_update_payload", "generate",
1076 "--payload", payload_file,
1077 "--target_image", target_file]
1078 if source_file is not None:
1079 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001080 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1081 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001082 assert p1.returncode == 0, "brillo_update_payload generate failed"
1083
1084 # 2. Generate hashes of the payload and metadata files.
1085 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1086 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1087 cmd = ["brillo_update_payload", "hash",
1088 "--unsigned_payload", payload_file,
1089 "--signature_size", "256",
1090 "--metadata_hash_file", metadata_sig_file,
1091 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001092 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1093 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001094 assert p1.returncode == 0, "brillo_update_payload hash failed"
1095
1096 # 3. Sign the hashes and insert them back into the payload file.
1097 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1098 suffix=".bin")
1099 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1100 suffix=".bin")
1101 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001102 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001103 cmd = [OPTIONS.payload_signer]
1104 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001105 else:
1106 cmd = ["openssl", "pkeyutl", "-sign",
1107 "-inkey", rsa_key,
1108 "-pkeyopt", "digest:sha256"]
1109 cmd.extend(["-in", payload_sig_file,
1110 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001111 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1112 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001113 assert p1.returncode == 0, "openssl sign payload failed"
1114
1115 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001116 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001117 cmd = [OPTIONS.payload_signer]
1118 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001119 else:
1120 cmd = ["openssl", "pkeyutl", "-sign",
1121 "-inkey", rsa_key,
1122 "-pkeyopt", "digest:sha256"]
1123 cmd.extend(["-in", metadata_sig_file,
1124 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001125 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1126 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001127 assert p1.returncode == 0, "openssl sign metadata failed"
1128
1129 # 3c. Insert the signatures back into the payload file.
1130 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1131 suffix=".bin")
1132 cmd = ["brillo_update_payload", "sign",
1133 "--unsigned_payload", payload_file,
1134 "--payload", signed_payload_file,
1135 "--signature_size", "256",
1136 "--metadata_signature_file", signed_metadata_sig_file,
1137 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001138 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1139 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001140 assert p1.returncode == 0, "brillo_update_payload sign failed"
1141
Alex Deymo19241c12016-02-04 22:29:29 -08001142 # 4. Dump the signed payload properties.
1143 properties_file = common.MakeTempFile(prefix="payload-properties-",
1144 suffix=".txt")
1145 cmd = ["brillo_update_payload", "properties",
1146 "--payload", signed_payload_file,
1147 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001148 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1149 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001150 assert p1.returncode == 0, "brillo_update_payload properties failed"
1151
Tao Bao7c5dc572016-06-14 17:48:11 -07001152 if OPTIONS.wipe_user_data:
1153 with open(properties_file, "a") as f:
1154 f.write("POWERWASH=1\n")
1155 metadata["ota-wipe"] = "yes"
1156
Tao Baoc96316c2017-01-24 22:10:49 -08001157 # Add the signed payload file and properties into the zip. In order to
1158 # support streaming, we pack payload.bin, payload_properties.txt and
1159 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1160 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001161 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1162 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001163 common.ZipWrite(output_zip, properties_file,
1164 arcname="payload_properties.txt",
1165 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001166
Tianjie Xucfa86222016-03-07 16:31:19 -08001167 # If dm-verity is supported for the device, copy contents of care_map
1168 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001169 target_zip = zipfile.ZipFile(target_file, "r")
Tianjie Xucfa86222016-03-07 16:31:19 -08001170 if OPTIONS.info_dict.get("verity") == "true":
Tianjie Xucfa86222016-03-07 16:31:19 -08001171 care_map_path = "META/care_map.txt"
1172 namelist = target_zip.namelist()
1173 if care_map_path in namelist:
1174 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001175 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1176 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001177 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001178 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001179
1180 if HasVendorPartition(target_zip):
1181 update_vendor = True
1182 update_system = True
1183
1184 # If incremental then figure out what is being updated so metadata only for
1185 # the updated image is included.
1186 if source_file is not None:
1187 input_tmp, input_zip = common.UnzipTemp(
1188 target_file, UNZIP_PATTERN)
1189 source_tmp, source_zip = common.UnzipTemp(
1190 source_file, UNZIP_PATTERN)
1191
1192 vendor_src = GetImage("vendor", source_tmp)
1193 vendor_tgt = GetImage("vendor", input_tmp)
1194 system_src = GetImage("system", source_tmp)
1195 system_tgt = GetImage("system", input_tmp)
1196
1197 update_system = system_src.TotalSha1() != system_tgt.TotalSha1()
1198 update_vendor = vendor_src.TotalSha1() != vendor_tgt.TotalSha1()
1199
1200 input_zip.close()
1201 source_zip.close()
1202
1203 target_zip = zipfile.ZipFile(target_file, "r")
1204 AddCompatibilityArchive(target_zip, output_zip, update_system,
1205 update_vendor)
1206 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001207
Tao Bao2dd1c482017-02-03 16:49:39 -08001208 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001209 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001210 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001211 WriteMetadata(metadata, output_zip)
1212 common.ZipClose(output_zip)
1213
Tao Bao2dd1c482017-02-03 16:49:39 -08001214 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1215 # zip entries, as well as padding the entry headers. We do a preliminary
1216 # signing (with an incomplete metadata entry) to allow that to happen. Then
1217 # compute the zip entry offsets, write back the final metadata and do the
1218 # final signing.
1219 prelim_signing = tempfile.NamedTemporaryFile()
1220 SignOutput(temp_zip_file.name, prelim_signing.name)
1221 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001222
Tao Bao2dd1c482017-02-03 16:49:39 -08001223 # Open the signed zip. Compute the final metadata that's needed for streaming.
1224 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1225 compression=zipfile.ZIP_DEFLATED)
1226 expected_length = len(metadata['ota-streaming-property-files'])
1227 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1228 prelim_zip, reserve_space=False, expected_length=expected_length)
1229
1230 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1231 final_signing = tempfile.NamedTemporaryFile()
1232 output_zip = zipfile.ZipFile(final_signing, "w",
1233 compression=zipfile.ZIP_DEFLATED)
1234 for item in prelim_zip.infolist():
1235 if item.filename == METADATA_NAME:
1236 continue
1237
1238 data = prelim_zip.read(item.filename)
1239 out_info = copy.copy(item)
1240 common.ZipWriteStr(output_zip, out_info, data)
1241
1242 # Now write the final metadata entry.
1243 WriteMetadata(metadata, output_zip)
1244 common.ZipClose(prelim_zip)
1245 common.ZipClose(output_zip)
1246
1247 # Re-sign the package after updating the metadata entry.
1248 SignOutput(final_signing.name, output_file)
1249 final_signing.close()
1250
1251 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001252 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001253 actual = metadata['ota-streaming-property-files'].strip()
1254 expected = ComputeStreamingMetadata(output_zip)
1255 assert actual == expected, \
1256 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001257 common.ZipClose(output_zip)
1258
Tao Baoc098e9e2016-01-07 13:03:56 -08001259
Doug Zongkereef39442009-04-02 12:14:19 -07001260def main(argv):
1261
1262 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001263 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001264 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001265 elif o in ("-k", "--package_key"):
1266 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001267 elif o in ("-i", "--incremental_from"):
1268 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001269 elif o == "--full_radio":
1270 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001271 elif o == "--full_bootloader":
1272 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001273 elif o in ("-w", "--wipe_user_data"):
1274 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001275 elif o == "--downgrade":
1276 OPTIONS.downgrade = True
1277 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001278 elif o == "--override_timestamp":
1279 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001280 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001281 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001282 elif o == "--oem_no_mount":
1283 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001284 elif o in ("-e", "--extra_script"):
1285 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001286 elif o in ("-t", "--worker_threads"):
1287 if a.isdigit():
1288 OPTIONS.worker_threads = int(a)
1289 else:
1290 raise ValueError("Cannot parse value %r for option %r - only "
1291 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001292 elif o in ("-2", "--two_step"):
1293 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001294 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001295 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001296 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001297 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001298 elif o == "--block":
1299 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001300 elif o in ("-b", "--binary"):
1301 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001302 elif o in ("--no_fallback_to_full",):
1303 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001304 elif o == "--stash_threshold":
1305 try:
1306 OPTIONS.stash_threshold = float(a)
1307 except ValueError:
1308 raise ValueError("Cannot parse value %r for option %r - expecting "
1309 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001310 elif o == "--gen_verify":
1311 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001312 elif o == "--log_diff":
1313 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001314 elif o == "--payload_signer":
1315 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001316 elif o == "--payload_signer_args":
1317 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001318 elif o == "--extracted_input_target_files":
1319 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001320 else:
1321 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001322 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001323
1324 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08001325 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001326 extra_long_opts=[
1327 "board_config=",
1328 "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",
1344 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001345 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001346 "gen_verify",
1347 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001348 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001349 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001350 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001351 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001352
1353 if len(args) != 2:
1354 common.Usage(__doc__)
1355 sys.exit(1)
1356
Tao Bao5d182562016-02-23 11:38:39 -08001357 if OPTIONS.downgrade:
1358 # Sanity check to enforce a data wipe.
1359 if not OPTIONS.wipe_user_data:
1360 raise ValueError("Cannot downgrade without a data wipe")
1361
1362 # We should only allow downgrading incrementals (as opposed to full).
1363 # Otherwise the device may go back from arbitrary build with this full
1364 # OTA package.
1365 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001366 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001367
Tao Bao3e6161a2017-02-28 11:48:48 -08001368 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1369 "Cannot have --downgrade AND --override_timestamp both"
1370
Tao Baoc098e9e2016-01-07 13:03:56 -08001371 # Load the dict file from the zip directly to have a peek at the OTA type.
1372 # For packages using A/B update, unzipping is not needed.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001373 if OPTIONS.extracted_input is not None:
1374 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input)
1375 else:
1376 input_zip = zipfile.ZipFile(args[0], "r")
1377 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1378 common.ZipClose(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001379
1380 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1381
Christian Oderf63e2cd2017-05-01 22:30:15 +02001382 # Use the default key to sign the package if not specified with package_key.
1383 # package_keys are needed on ab_updates, so always define them if an
1384 # ab_update is getting created.
1385 if not OPTIONS.no_signing or ab_update:
1386 if OPTIONS.package_key is None:
1387 OPTIONS.package_key = OPTIONS.info_dict.get(
1388 "default_system_dev_certificate",
1389 "build/target/product/security/testkey")
1390 # Get signing keys
1391 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1392
Tao Baoc098e9e2016-01-07 13:03:56 -08001393 if ab_update:
1394 if OPTIONS.incremental_source is not None:
1395 OPTIONS.target_info_dict = OPTIONS.info_dict
1396 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1397 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1398 common.ZipClose(source_zip)
1399
1400 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001401 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001402 common.DumpInfoDict(OPTIONS.info_dict)
1403
1404 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001405 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001406 common.DumpInfoDict(OPTIONS.source_info_dict)
1407
1408 WriteABOTAPackageWithBrilloScript(
1409 target_file=args[0],
1410 output_file=args[1],
1411 source_file=OPTIONS.incremental_source)
1412
Tao Bao89fbb0f2017-01-10 10:47:58 -08001413 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001414 return
1415
Doug Zongker1c390a22009-05-14 19:06:36 -07001416 if OPTIONS.extra_script is not None:
1417 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1418
Dan Willemsencea5cd22017-03-21 14:44:27 -07001419 if OPTIONS.extracted_input is not None:
1420 OPTIONS.input_tmp = OPTIONS.extracted_input
1421 OPTIONS.target_tmp = OPTIONS.input_tmp
1422 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp)
1423 input_zip = zipfile.ZipFile(args[0], "r")
1424 else:
1425 print("unzipping target target-files...")
1426 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1427 args[0], UNZIP_PATTERN)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001428
Dan Willemsencea5cd22017-03-21 14:44:27 -07001429 OPTIONS.target_tmp = OPTIONS.input_tmp
1430 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001431
Doug Zongker37974732010-09-16 17:44:38 -07001432 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001433 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07001434 common.DumpInfoDict(OPTIONS.info_dict)
1435
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001436 # If the caller explicitly specified the device-specific extensions
1437 # path via -s/--device_specific, use that. Otherwise, use
1438 # META/releasetools.py if it is present in the target target_files.
1439 # Otherwise, take the path of the file from 'tool_extensions' in the
1440 # info dict and look for that in the local filesystem, relative to
1441 # the current directory.
1442
Doug Zongker37974732010-09-16 17:44:38 -07001443 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001444 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1445 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001446 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001447 OPTIONS.device_specific = from_input
1448 else:
1449 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1450
Doug Zongker37974732010-09-16 17:44:38 -07001451 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001452 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001453
Tao Baoc098e9e2016-01-07 13:03:56 -08001454 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001455 raise common.ExternalError(
1456 "--- target build has specified no recovery ---")
1457
Tao Bao767e3ac2015-11-10 12:19:19 -08001458 # Set up the output zip. Create a temporary zip file if signing is needed.
1459 if OPTIONS.no_signing:
1460 if os.path.exists(args[1]):
1461 os.unlink(args[1])
1462 output_zip = zipfile.ZipFile(args[1], "w",
1463 compression=zipfile.ZIP_DEFLATED)
1464 else:
1465 temp_zip_file = tempfile.NamedTemporaryFile()
1466 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1467 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001468
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001469 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001470 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001471 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001472 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001473 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001474
Tao Bao9bc6bb22015-11-09 16:58:28 -08001475 # Generate a verify package.
1476 if OPTIONS.gen_verify:
1477 WriteVerifyPackage(input_zip, output_zip)
1478
Tao Bao767e3ac2015-11-10 12:19:19 -08001479 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001480 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001481 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001482
1483 # Generate an incremental OTA. It will fall back to generate a full OTA on
1484 # failure unless no_fallback_to_full is specified.
1485 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001486 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001487 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001488 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001489 UNZIP_PATTERN)
Tao Bao767e3ac2015-11-10 12:19:19 -08001490 OPTIONS.target_info_dict = OPTIONS.info_dict
1491 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1492 OPTIONS.source_tmp)
1493 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001494 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001495 common.DumpInfoDict(OPTIONS.source_info_dict)
1496 try:
Tao Bao457cbf62017-03-06 09:56:01 -08001497 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001498 if OPTIONS.log_diff:
1499 out_file = open(OPTIONS.log_diff, 'w')
1500 import target_files_diff
1501 target_files_diff.recursiveDiff('',
1502 OPTIONS.source_tmp,
1503 OPTIONS.input_tmp,
1504 out_file)
1505 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001506 except ValueError:
1507 if not OPTIONS.fallback_to_full:
1508 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08001509 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001510 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001511 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001512
Tao Bao767e3ac2015-11-10 12:19:19 -08001513 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001514
Tao Bao767e3ac2015-11-10 12:19:19 -08001515 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001516 if not OPTIONS.no_signing:
1517 SignOutput(temp_zip_file.name, args[1])
1518 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001519
Tao Bao89fbb0f2017-01-10 10:47:58 -08001520 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001521
1522
1523if __name__ == '__main__':
1524 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001525 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001526 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001527 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001528 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001529 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001530 finally:
1531 common.Cleanup()