blob: c3f063fcc85e157e894477e18aa657c35fecdeb2 [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 Bao8dcf7382015-05-21 14:09:49 -0700183
Tao Bao2dd1c482017-02-03 16:49:39 -0800184METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800185UNZIP_PATTERN = ['IMAGES/*', 'META/*']
186
Tao Bao2dd1c482017-02-03 16:49:39 -0800187
Doug Zongkereef39442009-04-02 12:14:19 -0700188def SignOutput(temp_zip_name, output_zip_name):
189 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
190 pw = key_passwords[OPTIONS.package_key]
191
Doug Zongker951495f2009-08-14 12:44:19 -0700192 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
193 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700194
195
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800196def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700197 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700198 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700199 device = GetBuildProp("ro.product.device", info_dict)
200 script.AssertDevice(device)
201 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800202 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700203 raise common.ExternalError(
204 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700205 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800206 values = []
207 for oem_dict in oem_dicts:
208 if oem_dict.get(prop):
209 values.append(oem_dict[prop])
210 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700211 raise common.ExternalError(
212 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800213 script.AssertOemProperty(prop, values)
214
215
Tao Baoebce6972017-03-06 10:22:20 -0800216def _LoadOemDicts(script, recovery_mount_options=None):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800217 """Returns the list of loaded OEM properties dict."""
218 oem_dicts = None
219 if OPTIONS.oem_source is None:
220 raise common.ExternalError("OEM source required for this build")
Tao Baoebce6972017-03-06 10:22:20 -0800221 if not OPTIONS.oem_no_mount and script:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800222 script.Mount("/oem", recovery_mount_options)
223 oem_dicts = []
224 for oem_file in OPTIONS.oem_source:
225 oem_dicts.append(common.LoadDictionaryFromLines(
226 open(oem_file).readlines()))
227 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700228
Doug Zongkereef39442009-04-02 12:14:19 -0700229
Tao Baod42e97e2016-11-30 12:11:57 -0800230def _WriteRecoveryImageToBoot(script, output_zip):
231 """Find and write recovery image to /boot in two-step OTA.
232
233 In two-step OTAs, we write recovery image to /boot as the first step so that
234 we can reboot to there and install a new recovery image to /recovery.
235 A special "recovery-two-step.img" will be preferred, which encodes the correct
236 path of "/boot". Otherwise the device may show "device is corrupt" message
237 when booting into /boot.
238
239 Fall back to using the regular recovery.img if the two-step recovery image
240 doesn't exist. Note that rebuilding the special image at this point may be
241 infeasible, because we don't have the desired boot signer and keys when
242 calling ota_from_target_files.py.
243 """
244
245 recovery_two_step_img_name = "recovery-two-step.img"
246 recovery_two_step_img_path = os.path.join(
247 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
248 if os.path.exists(recovery_two_step_img_path):
249 recovery_two_step_img = common.GetBootableImage(
250 recovery_two_step_img_name, recovery_two_step_img_name,
251 OPTIONS.input_tmp, "RECOVERY")
252 common.ZipWriteStr(
253 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800254 print("two-step package: using %s in stage 1/3" % (
255 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800256 script.WriteRawImage("/boot", recovery_two_step_img_name)
257 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800258 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800259 # The "recovery.img" entry has been written into package earlier.
260 script.WriteRawImage("/boot", "recovery.img")
261
262
Doug Zongkerc9253822014-02-04 12:17:58 -0800263def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700264 namelist = [name for name in target_files_zip.namelist()]
265 return ("SYSTEM/recovery-from-boot.p" in namelist or
266 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700267
Tao Bao457cbf62017-03-06 09:56:01 -0800268
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700269def HasVendorPartition(target_files_zip):
270 try:
271 target_files_zip.getinfo("VENDOR/")
272 return True
273 except KeyError:
274 return False
275
Tao Bao457cbf62017-03-06 09:56:01 -0800276
Michael Runge6e836112014-04-15 17:40:21 -0700277def GetOemProperty(name, oem_props, oem_dict, info_dict):
278 if oem_props is not None and name in oem_props:
279 return oem_dict[name]
280 return GetBuildProp(name, info_dict)
281
282
283def CalculateFingerprint(oem_props, oem_dict, info_dict):
284 if oem_props is None:
285 return GetBuildProp("ro.build.fingerprint", info_dict)
286 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700287 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
288 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
289 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
290 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700291
Doug Zongkerfc44a512014-08-26 13:10:25 -0700292
Tao Bao7e0f1602017-03-06 15:50:08 -0800293def GetImage(which, tmpdir):
294 """Returns an image object suitable for passing to BlockImageDiff.
295
296 'which' partition must be "system" or "vendor". A prebuilt image and file
297 map must already exist in tmpdir.
298 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700299
300 assert which in ("system", "vendor")
301
302 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700303 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700304
Tao Bao7e0f1602017-03-06 15:50:08 -0800305 # The image and map files must have been created prior to calling
306 # ota_from_target_files.py (since LMP).
307 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700308
Tao Baoff777812015-05-12 11:42:31 -0700309 # Bug: http://b/20939131
310 # In ext4 filesystems, block 0 might be changed even being mounted
311 # R/O. We add it to clobbered_blocks so that it will be written to the
312 # target unconditionally. Note that they are still part of care_map.
313 clobbered_blocks = "0"
314
315 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700316
317
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700318def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700319 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700320 # be installed on top of. For now, we expect the API just won't
321 # change very often. Similarly for fstab, it might have changed
322 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700323 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700324
Tao Bao838c68f2016-03-15 19:16:18 +0000325 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700326 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800327 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700328 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800329 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700330
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800331 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
332 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700333 metadata = {
Tao Bao39f3eaf2017-03-09 15:01:11 -0800334 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800335 "pre-device": GetOemProperty("ro.product.device", oem_props,
336 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700337 OPTIONS.info_dict),
338 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
339 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700340
Doug Zongker05d3dea2009-06-22 11:32:31 -0700341 device_specific = common.DeviceSpecificParams(
342 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700343 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700344 output_zip=output_zip,
345 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700346 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700347 metadata=metadata,
348 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700349
Tao Bao457cbf62017-03-06 09:56:01 -0800350 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800351
Tao Bao457cbf62017-03-06 09:56:01 -0800352 metadata["ota-type"] = "BLOCK"
Tao Baod8d14be2016-02-04 14:26:02 -0800353
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700354 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
355 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
356 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700357
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800358 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700359 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800360
361 # Two-step package strategy (in chronological order, which is *not*
362 # the order in which the generated script has things):
363 #
364 # if stage is not "2/3" or "3/3":
365 # write recovery image to boot partition
366 # set stage to "2/3"
367 # reboot to boot partition and restart recovery
368 # else if stage is "2/3":
369 # write recovery image to recovery partition
370 # set stage to "3/3"
371 # reboot to recovery partition and restart recovery
372 # else:
373 # (stage must be "3/3")
374 # set stage to ""
375 # do normal full package installation:
376 # wipe and install system, boot image, etc.
377 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700378 # complete script normally
379 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800380
381 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
382 OPTIONS.input_tmp, "RECOVERY")
383 if OPTIONS.two_step:
384 if not OPTIONS.info_dict.get("multistage_support", None):
385 assert False, "two-step packages not supported by this build"
386 fs = OPTIONS.info_dict["fstab"]["/misc"]
387 assert fs.fs_type.upper() == "EMMC", \
388 "two-step packages only supported on devices with EMMC /misc partitions"
389 bcb_dev = {"bcb_dev": fs.device}
390 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
391 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700392if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800393""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800394
395 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
396 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800397 script.WriteRawImage("/recovery", "recovery.img")
398 script.AppendExtra("""
399set_stage("%(bcb_dev)s", "3/3");
400reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700401else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800402""" % bcb_dev)
403
Tao Baod42e97e2016-11-30 12:11:57 -0800404 # Stage 3/3: Make changes.
405 script.Comment("Stage 3/3")
406
Tao Bao6c55a8a2015-04-08 15:30:27 -0700407 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700408 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700409
Doug Zongkere5ff5902012-01-17 10:55:37 -0800410 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700411
Doug Zongker01ce19c2014-02-04 13:48:15 -0800412 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700413
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700414 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800415 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700416 if HasVendorPartition(input_zip):
417 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700418
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400419 # Place a copy of file_contexts.bin into the OTA package which will be used
420 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700421 if "selinux_fc" in OPTIONS.info_dict:
422 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500423
Michael Runge7cd99ba2014-10-22 17:21:48 -0700424 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
425
Doug Zongker4b9596f2014-06-09 14:15:45 -0700426 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800427
Tao Bao457cbf62017-03-06 09:56:01 -0800428 # Full OTA is done as an "incremental" against an empty source image. This
429 # has the effect of writing new data from the package to the entire
430 # partition, but lets us reuse the updater code that writes incrementals to
431 # do it.
432 system_tgt = GetImage("system", OPTIONS.input_tmp)
433 system_tgt.ResetFileMap()
434 system_diff = common.BlockDifference("system", system_tgt, src=None)
435 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700436
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700437 boot_img = common.GetBootableImage(
438 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800439
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700440 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700441 script.ShowProgress(0.1, 0)
442
Tao Bao457cbf62017-03-06 09:56:01 -0800443 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
444 vendor_tgt.ResetFileMap()
445 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
446 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700447
Doug Zongker37974732010-09-16 17:44:38 -0700448 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700449 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700450
Doug Zongker01ce19c2014-02-04 13:48:15 -0800451 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700452 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700453
Doug Zongker01ce19c2014-02-04 13:48:15 -0800454 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700455 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700456
Doug Zongker1c390a22009-05-14 19:06:36 -0700457 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700458 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700459
Doug Zongker14833602010-02-02 13:12:04 -0800460 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800461
Doug Zongker922206e2014-03-04 13:16:24 -0800462 if OPTIONS.wipe_user_data:
463 script.ShowProgress(0.1, 10)
464 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700465
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800466 if OPTIONS.two_step:
467 script.AppendExtra("""
468set_stage("%(bcb_dev)s", "");
469""" % bcb_dev)
470 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800471
472 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
473 script.Comment("Stage 1/3")
474 _WriteRecoveryImageToBoot(script, output_zip)
475
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800476 script.AppendExtra("""
477set_stage("%(bcb_dev)s", "2/3");
478reboot_now("%(bcb_dev)s", "");
479endif;
480endif;
481""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800482
Tao Bao5d182562016-02-23 11:38:39 -0800483 script.SetProgress(1)
484 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800485 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700486 WriteMetadata(metadata, output_zip)
487
Doug Zongkerfc44a512014-08-26 13:10:25 -0700488
Dan Albert8e0178d2015-01-27 15:53:15 -0800489def WritePolicyConfig(file_name, output_zip):
490 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500491
Doug Zongker2ea21062010-04-28 16:05:21 -0700492
493def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800494 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
495 common.ZipWriteStr(output_zip, METADATA_NAME, value,
496 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700497
Doug Zongkerfc44a512014-08-26 13:10:25 -0700498
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700499def GetBuildProp(prop, info_dict):
500 """Return the fingerprint of the build of a given target-files info_dict."""
501 try:
502 return info_dict.get("build.prop", {})[prop]
503 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700504 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700505
Doug Zongkerfc44a512014-08-26 13:10:25 -0700506
Tao Baob31892e2017-02-07 11:21:17 -0800507def HandleDowngradeMetadata(metadata):
508 # Only incremental OTAs are allowed to reach here.
509 assert OPTIONS.incremental_source is not None
510
511 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
512 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
513 is_downgrade = long(post_timestamp) < long(pre_timestamp)
514
515 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800516 if not is_downgrade:
517 raise RuntimeError("--downgrade specified but no downgrade detected: "
518 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800519 metadata["ota-downgrade"] = "yes"
520 elif OPTIONS.timestamp:
521 if not is_downgrade:
522 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
523 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
524 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800525 else:
526 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800527 raise RuntimeError("Downgrade detected based on timestamp check: "
528 "pre: %s, post: %s. Need to specify --timestamp OR "
529 "--downgrade to allow building the incremental." % (
530 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800531 metadata["post-timestamp"] = post_timestamp
532
533
Geremy Condra36bd3652014-02-06 19:45:10 -0800534def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
535 source_version = OPTIONS.source_info_dict["recovery_api_version"]
536 target_version = OPTIONS.target_info_dict["recovery_api_version"]
537
538 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700539 print("WARNING: generating edify script for a source that "
540 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700541 script = edify_generator.EdifyGenerator(
542 source_version, OPTIONS.target_info_dict,
543 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800544
Tao Bao3806c232015-07-05 21:08:33 -0700545 recovery_mount_options = OPTIONS.source_info_dict.get(
546 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700547 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
548 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800549 oem_dicts = None
550 if source_oem_props and target_oem_props:
551 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700552
Dan Albert8b72aef2015-03-23 19:13:21 -0700553 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700554 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800555 oem_dicts and oem_dicts[0],
556 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800557 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700558 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800559
Tao Baob31892e2017-02-07 11:21:17 -0800560 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800561
Geremy Condra36bd3652014-02-06 19:45:10 -0800562 device_specific = common.DeviceSpecificParams(
563 source_zip=source_zip,
564 source_version=source_version,
565 target_zip=target_zip,
566 target_version=target_version,
567 output_zip=output_zip,
568 script=script,
569 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700570 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800571
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800572 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700573 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800574 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700575 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800576 metadata["pre-build"] = source_fp
577 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700578 metadata["pre-build-incremental"] = GetBuildProp(
579 "ro.build.version.incremental", OPTIONS.source_info_dict)
580 metadata["post-build-incremental"] = GetBuildProp(
581 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800582
583 source_boot = common.GetBootableImage(
584 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
585 OPTIONS.source_info_dict)
586 target_boot = common.GetBootableImage(
587 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
588 updating_boot = (not OPTIONS.two_step and
589 (source_boot.data != target_boot.data))
590
Geremy Condra36bd3652014-02-06 19:45:10 -0800591 target_recovery = common.GetBootableImage(
592 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800593
Tao Bao7e0f1602017-03-06 15:50:08 -0800594 system_src = GetImage("system", OPTIONS.source_tmp)
595 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700596
597 blockimgdiff_version = 1
598 if OPTIONS.info_dict:
599 blockimgdiff_version = max(
600 int(i) for i in
601 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
602
Tao Baof8acad12016-07-07 09:09:58 -0700603 # Check the first block of the source system partition for remount R/W only
604 # if the filesystem is ext4.
605 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
606 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700607 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
608 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
609 # b) the blocks listed in block map may not contain all the bytes for a given
610 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700611 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
612 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
613 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700614 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800615 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700616 version=blockimgdiff_version,
617 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700618
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700619 if HasVendorPartition(target_zip):
620 if not HasVendorPartition(source_zip):
621 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800622 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
623 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800624
625 # Check first block of vendor partition for remount R/W only if
626 # disk type is ext4
627 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800628 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700629 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700630 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800631 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700632 version=blockimgdiff_version,
633 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700634 else:
635 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800636
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800637 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800638 device_specific.IncrementalOTA_Assertions()
639
640 # Two-step incremental package strategy (in chronological order,
641 # which is *not* the order in which the generated script has
642 # things):
643 #
644 # if stage is not "2/3" or "3/3":
645 # do verification on current system
646 # write recovery image to boot partition
647 # set stage to "2/3"
648 # reboot to boot partition and restart recovery
649 # else if stage is "2/3":
650 # write recovery image to recovery partition
651 # set stage to "3/3"
652 # reboot to recovery partition and restart recovery
653 # else:
654 # (stage must be "3/3")
655 # perform update:
656 # patch system files, etc.
657 # force full install of new boot image
658 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700659 # complete script normally
660 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800661
662 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700663 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800664 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700665 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800666 assert fs.fs_type.upper() == "EMMC", \
667 "two-step packages only supported on devices with EMMC /misc partitions"
668 bcb_dev = {"bcb_dev": fs.device}
669 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
670 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700671if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800672""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800673
674 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
675 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700676 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800677 script.WriteRawImage("/recovery", "recovery.img")
678 script.AppendExtra("""
679set_stage("%(bcb_dev)s", "3/3");
680reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700681else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800682""" % bcb_dev)
683
Tao Baod42e97e2016-11-30 12:11:57 -0800684 # Stage 1/3: (a) Verify the current system.
685 script.Comment("Stage 1/3")
686
Tao Bao6c55a8a2015-04-08 15:30:27 -0700687 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800688 script.Print("Source: %s" % (source_fp,))
689 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700690
Geremy Condra36bd3652014-02-06 19:45:10 -0800691 script.Print("Verifying current system...")
692
693 device_specific.IncrementalOTA_VerifyBegin()
694
Tao Bao3e30d972016-03-15 13:20:19 -0700695 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
696 # patching on a device that's already on the target build will damage the
697 # system. Because operations like move don't check the block state, they
698 # always apply the changes unconditionally.
699 if blockimgdiff_version <= 2:
700 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700701 script.AssertSomeFingerprint(source_fp)
702 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700703 script.AssertSomeThumbprint(
704 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700705
706 else: # blockimgdiff_version > 2
707 if source_oem_props is None and target_oem_props is None:
708 script.AssertSomeFingerprint(source_fp, target_fp)
709 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700710 script.AssertSomeThumbprint(
711 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
712 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700713 elif source_oem_props is None and target_oem_props is not None:
714 script.AssertFingerprintOrThumbprint(
715 source_fp,
716 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
717 else:
718 script.AssertFingerprintOrThumbprint(
719 target_fp,
720 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800721
Tao Baod8d14be2016-02-04 14:26:02 -0800722 # Check the required cache size (i.e. stashed blocks).
723 size = []
724 if system_diff:
725 size.append(system_diff.required_cache)
726 if vendor_diff:
727 size.append(vendor_diff.required_cache)
728
Geremy Condra36bd3652014-02-06 19:45:10 -0800729 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700730 boot_type, boot_device = common.GetTypeAndDevice(
731 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800732 d = common.Difference(target_boot, source_boot)
733 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700734 if d is None:
735 include_full_boot = True
736 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
737 else:
738 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800739
Tao Bao89fbb0f2017-01-10 10:47:58 -0800740 print("boot target: %d source: %d diff: %d" % (
741 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800742
Doug Zongkerf8340082014-08-05 10:39:37 -0700743 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800744
Doug Zongkerf8340082014-08-05 10:39:37 -0700745 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
746 (boot_type, boot_device,
747 source_boot.size, source_boot.sha1,
748 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800749 size.append(target_boot.size)
750
751 if size:
752 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800753
754 device_specific.IncrementalOTA_VerifyEnd()
755
756 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800757 # Stage 1/3: (b) Write recovery image to /boot.
758 _WriteRecoveryImageToBoot(script, output_zip)
759
Geremy Condra36bd3652014-02-06 19:45:10 -0800760 script.AppendExtra("""
761set_stage("%(bcb_dev)s", "2/3");
762reboot_now("%(bcb_dev)s", "");
763else
764""" % bcb_dev)
765
Tao Baod42e97e2016-11-30 12:11:57 -0800766 # Stage 3/3: Make changes.
767 script.Comment("Stage 3/3")
768
Jesse Zhao75bcea02015-01-06 10:59:53 -0800769 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700770 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800771 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700772 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800773
Geremy Condra36bd3652014-02-06 19:45:10 -0800774 script.Comment("---- start making changes here ----")
775
776 device_specific.IncrementalOTA_InstallBegin()
777
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700778 system_diff.WriteScript(script, output_zip,
779 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700780
Doug Zongkerfc44a512014-08-26 13:10:25 -0700781 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700782 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800783
784 if OPTIONS.two_step:
785 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
786 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800787 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800788
789 if not OPTIONS.two_step:
790 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700791 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800792 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700793 script.Print("Installing boot image...")
794 script.WriteRawImage("/boot", "boot.img")
795 else:
796 # Produce the boot image by applying a patch to the current
797 # contents of the boot partition, and write it back to the
798 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -0800799 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700800 script.Print("Patching boot image...")
801 script.ShowProgress(0.1, 10)
802 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
803 % (boot_type, boot_device,
804 source_boot.size, source_boot.sha1,
805 target_boot.size, target_boot.sha1),
806 "-",
807 target_boot.size, target_boot.sha1,
808 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800809 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800810 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800811
812 # Do device-specific installation (eg, write radio image).
813 device_specific.IncrementalOTA_InstallEnd()
814
815 if OPTIONS.extra_script is not None:
816 script.AppendExtra(OPTIONS.extra_script)
817
Doug Zongker922206e2014-03-04 13:16:24 -0800818 if OPTIONS.wipe_user_data:
819 script.Print("Erasing user data...")
820 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -0800821 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -0800822
Geremy Condra36bd3652014-02-06 19:45:10 -0800823 if OPTIONS.two_step:
824 script.AppendExtra("""
825set_stage("%(bcb_dev)s", "");
826endif;
827endif;
828""" % bcb_dev)
829
830 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -0800831 # For downgrade OTAs, we prefer to use the update-binary in the source
832 # build that is actually newer than the one in the target build.
833 if OPTIONS.downgrade:
834 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
835 else:
836 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800837 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -0800838 WriteMetadata(metadata, output_zip)
839
Doug Zongker32b527d2014-03-04 10:03:02 -0800840
Tao Bao9bc6bb22015-11-09 16:58:28 -0800841def WriteVerifyPackage(input_zip, output_zip):
842 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
843
844 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
845 recovery_mount_options = OPTIONS.info_dict.get(
846 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800847 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700848 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800849 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800850
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800851 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
852 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800853 metadata = {
854 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800855 "pre-device": GetOemProperty("ro.product.device", oem_props,
856 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -0800857 OPTIONS.info_dict),
858 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
859 }
860
861 device_specific = common.DeviceSpecificParams(
862 input_zip=input_zip,
863 input_version=OPTIONS.info_dict["recovery_api_version"],
864 output_zip=output_zip,
865 script=script,
866 input_tmp=OPTIONS.input_tmp,
867 metadata=metadata,
868 info_dict=OPTIONS.info_dict)
869
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800870 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800871
872 script.Print("Verifying device images against %s..." % target_fp)
873 script.AppendExtra("")
874
875 script.Print("Verifying boot...")
876 boot_img = common.GetBootableImage(
877 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
878 boot_type, boot_device = common.GetTypeAndDevice(
879 "/boot", OPTIONS.info_dict)
880 script.Verify("%s:%s:%d:%s" % (
881 boot_type, boot_device, boot_img.size, boot_img.sha1))
882 script.AppendExtra("")
883
884 script.Print("Verifying recovery...")
885 recovery_img = common.GetBootableImage(
886 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
887 recovery_type, recovery_device = common.GetTypeAndDevice(
888 "/recovery", OPTIONS.info_dict)
889 script.Verify("%s:%s:%d:%s" % (
890 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
891 script.AppendExtra("")
892
Tao Bao7e0f1602017-03-06 15:50:08 -0800893 system_tgt = GetImage("system", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800894 system_tgt.ResetFileMap()
895 system_diff = common.BlockDifference("system", system_tgt, src=None)
896 system_diff.WriteStrictVerifyScript(script)
897
898 if HasVendorPartition(input_zip):
Tao Bao7e0f1602017-03-06 15:50:08 -0800899 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800900 vendor_tgt.ResetFileMap()
901 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
902 vendor_diff.WriteStrictVerifyScript(script)
903
904 # Device specific partitions, such as radio, bootloader and etc.
905 device_specific.VerifyOTA_Assertions()
906
907 script.SetProgress(1.0)
908 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800909 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800910 WriteMetadata(metadata, output_zip)
911
912
Tao Baoc098e9e2016-01-07 13:03:56 -0800913def WriteABOTAPackageWithBrilloScript(target_file, output_file,
914 source_file=None):
915 """Generate an Android OTA package that has A/B update payload."""
916
Tao Bao2dd1c482017-02-03 16:49:39 -0800917 def ComputeStreamingMetadata(zip_file, reserve_space=False,
918 expected_length=None):
919 """Compute the streaming metadata for a given zip.
920
921 When 'reserve_space' is True, we reserve extra space for the offset and
922 length of the metadata entry itself, although we don't know the final
923 values until the package gets signed. This function will be called again
924 after signing. We then write the actual values and pad the string to the
925 length we set earlier. Note that we can't use the actual length of the
926 metadata entry in the second run. Otherwise the offsets for other entries
927 will be changing again.
928 """
Tao Baoc96316c2017-01-24 22:10:49 -0800929
930 def ComputeEntryOffsetSize(name):
931 """Compute the zip entry offset and size."""
932 info = zip_file.getinfo(name)
933 offset = info.header_offset + len(info.FileHeader())
934 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -0800935 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -0800936
937 # payload.bin and payload_properties.txt must exist.
938 offsets = [ComputeEntryOffsetSize('payload.bin'),
939 ComputeEntryOffsetSize('payload_properties.txt')]
940
941 # care_map.txt is available only if dm-verity is enabled.
942 if 'care_map.txt' in zip_file.namelist():
943 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -0800944
945 # 'META-INF/com/android/metadata' is required. We don't know its actual
946 # offset and length (as well as the values for other entries). So we
947 # reserve 10-byte as a placeholder, which is to cover the space for metadata
948 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
949 # beginning of the zip), as well as the possible value changes in other
950 # entries.
951 if reserve_space:
952 offsets.append('metadata:' + ' ' * 10)
953 else:
954 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
955
956 value = ','.join(offsets)
957 if expected_length is not None:
958 assert len(value) <= expected_length, \
959 'Insufficient reserved space: reserved=%d, actual=%d' % (
960 expected_length, len(value))
961 value += ' ' * (expected_length - len(value))
962 return value
Tao Baoc96316c2017-01-24 22:10:49 -0800963
Alex Deymod8d96ec2016-06-10 16:38:31 -0700964 # The place where the output from the subprocess should go.
965 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
966
Tao Baoc098e9e2016-01-07 13:03:56 -0800967 # Setup signing keys.
968 if OPTIONS.package_key is None:
969 OPTIONS.package_key = OPTIONS.info_dict.get(
970 "default_system_dev_certificate",
971 "build/target/product/security/testkey")
972
Tao Baodea0f8b2016-06-20 17:55:06 -0700973 # A/B updater expects a signing key in RSA format. Gets the key ready for
974 # later use in step 3, unless a payload_signer has been specified.
975 if OPTIONS.payload_signer is None:
976 cmd = ["openssl", "pkcs8",
977 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
978 "-inform", "DER", "-nocrypt"]
979 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
980 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -0700981 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
982 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -0700983 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -0800984
Tao Baodea0f8b2016-06-20 17:55:06 -0700985 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -0800986 temp_zip_file = tempfile.NamedTemporaryFile()
987 output_zip = zipfile.ZipFile(temp_zip_file, "w",
988 compression=zipfile.ZIP_DEFLATED)
989
990 # Metadata to comply with Android OTA package format.
991 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800992 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -0800993 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800994 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -0800995
996 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800997 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -0800998 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -0700999 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1000 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001001 "pre-device": GetOemProperty("ro.product.device", oem_props,
1002 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001003 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001004 "ota-required-cache": "0",
1005 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001006 }
1007
1008 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001009 metadata["pre-build"] = CalculateFingerprint(oem_props,
1010 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001011 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001012 metadata["pre-build-incremental"] = GetBuildProp(
1013 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001014
Tao Baob31892e2017-02-07 11:21:17 -08001015 HandleDowngradeMetadata(metadata)
1016 else:
1017 metadata["post-timestamp"] = GetBuildProp(
1018 "ro.build.date.utc", OPTIONS.info_dict)
1019
Tao Baoc098e9e2016-01-07 13:03:56 -08001020 # 1. Generate payload.
1021 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1022 cmd = ["brillo_update_payload", "generate",
1023 "--payload", payload_file,
1024 "--target_image", target_file]
1025 if source_file is not None:
1026 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001027 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1028 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001029 assert p1.returncode == 0, "brillo_update_payload generate failed"
1030
1031 # 2. Generate hashes of the payload and metadata files.
1032 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1033 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1034 cmd = ["brillo_update_payload", "hash",
1035 "--unsigned_payload", payload_file,
1036 "--signature_size", "256",
1037 "--metadata_hash_file", metadata_sig_file,
1038 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001039 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1040 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001041 assert p1.returncode == 0, "brillo_update_payload hash failed"
1042
1043 # 3. Sign the hashes and insert them back into the payload file.
1044 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1045 suffix=".bin")
1046 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1047 suffix=".bin")
1048 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001049 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001050 cmd = [OPTIONS.payload_signer]
1051 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001052 else:
1053 cmd = ["openssl", "pkeyutl", "-sign",
1054 "-inkey", rsa_key,
1055 "-pkeyopt", "digest:sha256"]
1056 cmd.extend(["-in", payload_sig_file,
1057 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001058 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1059 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001060 assert p1.returncode == 0, "openssl sign payload failed"
1061
1062 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001063 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001064 cmd = [OPTIONS.payload_signer]
1065 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001066 else:
1067 cmd = ["openssl", "pkeyutl", "-sign",
1068 "-inkey", rsa_key,
1069 "-pkeyopt", "digest:sha256"]
1070 cmd.extend(["-in", metadata_sig_file,
1071 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001072 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1073 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001074 assert p1.returncode == 0, "openssl sign metadata failed"
1075
1076 # 3c. Insert the signatures back into the payload file.
1077 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1078 suffix=".bin")
1079 cmd = ["brillo_update_payload", "sign",
1080 "--unsigned_payload", payload_file,
1081 "--payload", signed_payload_file,
1082 "--signature_size", "256",
1083 "--metadata_signature_file", signed_metadata_sig_file,
1084 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001085 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1086 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001087 assert p1.returncode == 0, "brillo_update_payload sign failed"
1088
Alex Deymo19241c12016-02-04 22:29:29 -08001089 # 4. Dump the signed payload properties.
1090 properties_file = common.MakeTempFile(prefix="payload-properties-",
1091 suffix=".txt")
1092 cmd = ["brillo_update_payload", "properties",
1093 "--payload", signed_payload_file,
1094 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001095 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1096 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001097 assert p1.returncode == 0, "brillo_update_payload properties failed"
1098
Tao Bao7c5dc572016-06-14 17:48:11 -07001099 if OPTIONS.wipe_user_data:
1100 with open(properties_file, "a") as f:
1101 f.write("POWERWASH=1\n")
1102 metadata["ota-wipe"] = "yes"
1103
Tao Baoc96316c2017-01-24 22:10:49 -08001104 # Add the signed payload file and properties into the zip. In order to
1105 # support streaming, we pack payload.bin, payload_properties.txt and
1106 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1107 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001108 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1109 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001110 common.ZipWrite(output_zip, properties_file,
1111 arcname="payload_properties.txt",
1112 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001113
Tianjie Xucfa86222016-03-07 16:31:19 -08001114 # If dm-verity is supported for the device, copy contents of care_map
1115 # into A/B OTA package.
1116 if OPTIONS.info_dict.get("verity") == "true":
1117 target_zip = zipfile.ZipFile(target_file, "r")
1118 care_map_path = "META/care_map.txt"
1119 namelist = target_zip.namelist()
1120 if care_map_path in namelist:
1121 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001122 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1123 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001124 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001125 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001126 common.ZipClose(target_zip)
1127
Tao Bao2dd1c482017-02-03 16:49:39 -08001128 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001129 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001130 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001131 WriteMetadata(metadata, output_zip)
1132 common.ZipClose(output_zip)
1133
Tao Bao2dd1c482017-02-03 16:49:39 -08001134 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1135 # zip entries, as well as padding the entry headers. We do a preliminary
1136 # signing (with an incomplete metadata entry) to allow that to happen. Then
1137 # compute the zip entry offsets, write back the final metadata and do the
1138 # final signing.
1139 prelim_signing = tempfile.NamedTemporaryFile()
1140 SignOutput(temp_zip_file.name, prelim_signing.name)
1141 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001142
Tao Bao2dd1c482017-02-03 16:49:39 -08001143 # Open the signed zip. Compute the final metadata that's needed for streaming.
1144 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1145 compression=zipfile.ZIP_DEFLATED)
1146 expected_length = len(metadata['ota-streaming-property-files'])
1147 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1148 prelim_zip, reserve_space=False, expected_length=expected_length)
1149
1150 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1151 final_signing = tempfile.NamedTemporaryFile()
1152 output_zip = zipfile.ZipFile(final_signing, "w",
1153 compression=zipfile.ZIP_DEFLATED)
1154 for item in prelim_zip.infolist():
1155 if item.filename == METADATA_NAME:
1156 continue
1157
1158 data = prelim_zip.read(item.filename)
1159 out_info = copy.copy(item)
1160 common.ZipWriteStr(output_zip, out_info, data)
1161
1162 # Now write the final metadata entry.
1163 WriteMetadata(metadata, output_zip)
1164 common.ZipClose(prelim_zip)
1165 common.ZipClose(output_zip)
1166
1167 # Re-sign the package after updating the metadata entry.
1168 SignOutput(final_signing.name, output_file)
1169 final_signing.close()
1170
1171 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001172 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001173 actual = metadata['ota-streaming-property-files'].strip()
1174 expected = ComputeStreamingMetadata(output_zip)
1175 assert actual == expected, \
1176 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001177 common.ZipClose(output_zip)
1178
Tao Baoc098e9e2016-01-07 13:03:56 -08001179
Doug Zongkereef39442009-04-02 12:14:19 -07001180def main(argv):
1181
1182 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001183 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001184 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001185 elif o in ("-k", "--package_key"):
1186 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001187 elif o in ("-i", "--incremental_from"):
1188 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001189 elif o == "--full_radio":
1190 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001191 elif o == "--full_bootloader":
1192 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001193 elif o in ("-w", "--wipe_user_data"):
1194 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001195 elif o == "--downgrade":
1196 OPTIONS.downgrade = True
1197 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001198 elif o == "--override_timestamp":
1199 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001200 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001201 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001202 elif o == "--oem_no_mount":
1203 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001204 elif o in ("-e", "--extra_script"):
1205 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001206 elif o in ("-t", "--worker_threads"):
1207 if a.isdigit():
1208 OPTIONS.worker_threads = int(a)
1209 else:
1210 raise ValueError("Cannot parse value %r for option %r - only "
1211 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001212 elif o in ("-2", "--two_step"):
1213 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001214 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001215 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001216 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001217 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001218 elif o == "--block":
1219 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001220 elif o in ("-b", "--binary"):
1221 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001222 elif o in ("--no_fallback_to_full",):
1223 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001224 elif o == "--stash_threshold":
1225 try:
1226 OPTIONS.stash_threshold = float(a)
1227 except ValueError:
1228 raise ValueError("Cannot parse value %r for option %r - expecting "
1229 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001230 elif o == "--gen_verify":
1231 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001232 elif o == "--log_diff":
1233 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001234 elif o == "--payload_signer":
1235 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001236 elif o == "--payload_signer_args":
1237 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001238 elif o == "--extracted_input_target_files":
1239 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001240 else:
1241 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001242 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001243
1244 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08001245 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001246 extra_long_opts=[
1247 "board_config=",
1248 "package_key=",
1249 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001250 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001251 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001252 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001253 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001254 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001255 "extra_script=",
1256 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001257 "two_step",
1258 "no_signing",
1259 "block",
1260 "binary=",
1261 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001262 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001263 "verify",
1264 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001265 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001266 "gen_verify",
1267 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001268 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001269 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001270 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001271 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001272
1273 if len(args) != 2:
1274 common.Usage(__doc__)
1275 sys.exit(1)
1276
Tao Bao5d182562016-02-23 11:38:39 -08001277 if OPTIONS.downgrade:
1278 # Sanity check to enforce a data wipe.
1279 if not OPTIONS.wipe_user_data:
1280 raise ValueError("Cannot downgrade without a data wipe")
1281
1282 # We should only allow downgrading incrementals (as opposed to full).
1283 # Otherwise the device may go back from arbitrary build with this full
1284 # OTA package.
1285 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001286 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001287
Tao Bao3e6161a2017-02-28 11:48:48 -08001288 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1289 "Cannot have --downgrade AND --override_timestamp both"
1290
Tao Baoc098e9e2016-01-07 13:03:56 -08001291 # Load the dict file from the zip directly to have a peek at the OTA type.
1292 # For packages using A/B update, unzipping is not needed.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001293 if OPTIONS.extracted_input is not None:
1294 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input)
1295 else:
1296 input_zip = zipfile.ZipFile(args[0], "r")
1297 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1298 common.ZipClose(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001299
1300 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1301
1302 if ab_update:
1303 if OPTIONS.incremental_source is not None:
1304 OPTIONS.target_info_dict = OPTIONS.info_dict
1305 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1306 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1307 common.ZipClose(source_zip)
1308
1309 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001310 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001311 common.DumpInfoDict(OPTIONS.info_dict)
1312
1313 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001314 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001315 common.DumpInfoDict(OPTIONS.source_info_dict)
1316
1317 WriteABOTAPackageWithBrilloScript(
1318 target_file=args[0],
1319 output_file=args[1],
1320 source_file=OPTIONS.incremental_source)
1321
Tao Bao89fbb0f2017-01-10 10:47:58 -08001322 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001323 return
1324
Doug Zongker1c390a22009-05-14 19:06:36 -07001325 if OPTIONS.extra_script is not None:
1326 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1327
Dan Willemsencea5cd22017-03-21 14:44:27 -07001328 if OPTIONS.extracted_input is not None:
1329 OPTIONS.input_tmp = OPTIONS.extracted_input
1330 OPTIONS.target_tmp = OPTIONS.input_tmp
1331 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp)
1332 input_zip = zipfile.ZipFile(args[0], "r")
1333 else:
1334 print("unzipping target target-files...")
1335 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1336 args[0], UNZIP_PATTERN)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001337
Dan Willemsencea5cd22017-03-21 14:44:27 -07001338 OPTIONS.target_tmp = OPTIONS.input_tmp
1339 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001340
Doug Zongker37974732010-09-16 17:44:38 -07001341 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001342 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07001343 common.DumpInfoDict(OPTIONS.info_dict)
1344
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001345 # If the caller explicitly specified the device-specific extensions
1346 # path via -s/--device_specific, use that. Otherwise, use
1347 # META/releasetools.py if it is present in the target target_files.
1348 # Otherwise, take the path of the file from 'tool_extensions' in the
1349 # info dict and look for that in the local filesystem, relative to
1350 # the current directory.
1351
Doug Zongker37974732010-09-16 17:44:38 -07001352 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001353 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1354 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001355 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001356 OPTIONS.device_specific = from_input
1357 else:
1358 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1359
Doug Zongker37974732010-09-16 17:44:38 -07001360 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001361 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001362
Tao Baoc098e9e2016-01-07 13:03:56 -08001363 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001364 raise common.ExternalError(
1365 "--- target build has specified no recovery ---")
1366
Tao Bao767e3ac2015-11-10 12:19:19 -08001367 # Use the default key to sign the package if not specified with package_key.
1368 if not OPTIONS.no_signing:
1369 if OPTIONS.package_key is None:
1370 OPTIONS.package_key = OPTIONS.info_dict.get(
1371 "default_system_dev_certificate",
1372 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001373
Tao Bao767e3ac2015-11-10 12:19:19 -08001374 # Set up the output zip. Create a temporary zip file if signing is needed.
1375 if OPTIONS.no_signing:
1376 if os.path.exists(args[1]):
1377 os.unlink(args[1])
1378 output_zip = zipfile.ZipFile(args[1], "w",
1379 compression=zipfile.ZIP_DEFLATED)
1380 else:
1381 temp_zip_file = tempfile.NamedTemporaryFile()
1382 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1383 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001384
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001385 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001386 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001387 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001388 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001389 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001390
Tao Bao9bc6bb22015-11-09 16:58:28 -08001391 # Generate a verify package.
1392 if OPTIONS.gen_verify:
1393 WriteVerifyPackage(input_zip, output_zip)
1394
Tao Bao767e3ac2015-11-10 12:19:19 -08001395 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001396 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001397 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001398
1399 # Generate an incremental OTA. It will fall back to generate a full OTA on
1400 # failure unless no_fallback_to_full is specified.
1401 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001402 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001403 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001404 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001405 UNZIP_PATTERN)
Tao Bao767e3ac2015-11-10 12:19:19 -08001406 OPTIONS.target_info_dict = OPTIONS.info_dict
1407 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1408 OPTIONS.source_tmp)
1409 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001410 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001411 common.DumpInfoDict(OPTIONS.source_info_dict)
1412 try:
Tao Bao457cbf62017-03-06 09:56:01 -08001413 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001414 if OPTIONS.log_diff:
1415 out_file = open(OPTIONS.log_diff, 'w')
1416 import target_files_diff
1417 target_files_diff.recursiveDiff('',
1418 OPTIONS.source_tmp,
1419 OPTIONS.input_tmp,
1420 out_file)
1421 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001422 except ValueError:
1423 if not OPTIONS.fallback_to_full:
1424 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08001425 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001426 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001427 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001428
Tao Bao767e3ac2015-11-10 12:19:19 -08001429 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001430
Tao Bao767e3ac2015-11-10 12:19:19 -08001431 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001432 if not OPTIONS.no_signing:
1433 SignOutput(temp_zip_file.name, args[1])
1434 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001435
Tao Bao89fbb0f2017-01-10 10:47:58 -08001436 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001437
1438
1439if __name__ == '__main__':
1440 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001441 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001442 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001443 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001444 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001445 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001446 finally:
1447 common.Cleanup()