blob: 7e970a96f45191242a608af16e3b70860b342414 [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 = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800334 "pre-device": GetOemProperty("ro.product.device", oem_props,
335 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700336 OPTIONS.info_dict),
337 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
338 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700339
Doug Zongker05d3dea2009-06-22 11:32:31 -0700340 device_specific = common.DeviceSpecificParams(
341 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700342 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700343 output_zip=output_zip,
344 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700345 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700346 metadata=metadata,
347 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700348
Tao Bao457cbf62017-03-06 09:56:01 -0800349 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800350
Tao Bao457cbf62017-03-06 09:56:01 -0800351 metadata["ota-type"] = "BLOCK"
Tao Baod8d14be2016-02-04 14:26:02 -0800352
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700353 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
354 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
355 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700356
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800357 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700358 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800359
360 # Two-step package strategy (in chronological order, which is *not*
361 # the order in which the generated script has things):
362 #
363 # if stage is not "2/3" or "3/3":
364 # write recovery image to boot partition
365 # set stage to "2/3"
366 # reboot to boot partition and restart recovery
367 # else if stage is "2/3":
368 # write recovery image to recovery partition
369 # set stage to "3/3"
370 # reboot to recovery partition and restart recovery
371 # else:
372 # (stage must be "3/3")
373 # set stage to ""
374 # do normal full package installation:
375 # wipe and install system, boot image, etc.
376 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700377 # complete script normally
378 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800379
380 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
381 OPTIONS.input_tmp, "RECOVERY")
382 if OPTIONS.two_step:
383 if not OPTIONS.info_dict.get("multistage_support", None):
384 assert False, "two-step packages not supported by this build"
385 fs = OPTIONS.info_dict["fstab"]["/misc"]
386 assert fs.fs_type.upper() == "EMMC", \
387 "two-step packages only supported on devices with EMMC /misc partitions"
388 bcb_dev = {"bcb_dev": fs.device}
389 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
390 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700391if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800392""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800393
394 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
395 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800396 script.WriteRawImage("/recovery", "recovery.img")
397 script.AppendExtra("""
398set_stage("%(bcb_dev)s", "3/3");
399reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700400else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800401""" % bcb_dev)
402
Tao Baod42e97e2016-11-30 12:11:57 -0800403 # Stage 3/3: Make changes.
404 script.Comment("Stage 3/3")
405
Tao Bao6c55a8a2015-04-08 15:30:27 -0700406 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700407 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700408
Doug Zongkere5ff5902012-01-17 10:55:37 -0800409 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700410
Doug Zongker01ce19c2014-02-04 13:48:15 -0800411 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700412
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700413 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800414 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700415 if HasVendorPartition(input_zip):
416 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700417
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400418 # Place a copy of file_contexts.bin into the OTA package which will be used
419 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700420 if "selinux_fc" in OPTIONS.info_dict:
421 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500422
Michael Runge7cd99ba2014-10-22 17:21:48 -0700423 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
424
Doug Zongker4b9596f2014-06-09 14:15:45 -0700425 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800426
Tao Bao457cbf62017-03-06 09:56:01 -0800427 # Full OTA is done as an "incremental" against an empty source image. This
428 # has the effect of writing new data from the package to the entire
429 # partition, but lets us reuse the updater code that writes incrementals to
430 # do it.
431 system_tgt = GetImage("system", OPTIONS.input_tmp)
432 system_tgt.ResetFileMap()
433 system_diff = common.BlockDifference("system", system_tgt, src=None)
434 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700435
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700436 boot_img = common.GetBootableImage(
437 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800438
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700439 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700440 script.ShowProgress(0.1, 0)
441
Tao Bao457cbf62017-03-06 09:56:01 -0800442 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
443 vendor_tgt.ResetFileMap()
444 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
445 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700446
Doug Zongker37974732010-09-16 17:44:38 -0700447 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700448 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700449
Doug Zongker01ce19c2014-02-04 13:48:15 -0800450 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700451 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700452
Doug Zongker01ce19c2014-02-04 13:48:15 -0800453 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700454 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700455
Doug Zongker1c390a22009-05-14 19:06:36 -0700456 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700457 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700458
Doug Zongker14833602010-02-02 13:12:04 -0800459 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800460
Doug Zongker922206e2014-03-04 13:16:24 -0800461 if OPTIONS.wipe_user_data:
462 script.ShowProgress(0.1, 10)
463 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700464
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800465 if OPTIONS.two_step:
466 script.AppendExtra("""
467set_stage("%(bcb_dev)s", "");
468""" % bcb_dev)
469 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800470
471 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
472 script.Comment("Stage 1/3")
473 _WriteRecoveryImageToBoot(script, output_zip)
474
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800475 script.AppendExtra("""
476set_stage("%(bcb_dev)s", "2/3");
477reboot_now("%(bcb_dev)s", "");
478endif;
479endif;
480""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800481
Tao Bao5d182562016-02-23 11:38:39 -0800482 script.SetProgress(1)
483 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800484 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700485 WriteMetadata(metadata, output_zip)
486
Doug Zongkerfc44a512014-08-26 13:10:25 -0700487
Dan Albert8e0178d2015-01-27 15:53:15 -0800488def WritePolicyConfig(file_name, output_zip):
489 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500490
Doug Zongker2ea21062010-04-28 16:05:21 -0700491
492def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800493 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
494 common.ZipWriteStr(output_zip, METADATA_NAME, value,
495 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700496
Doug Zongkerfc44a512014-08-26 13:10:25 -0700497
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700498def GetBuildProp(prop, info_dict):
499 """Return the fingerprint of the build of a given target-files info_dict."""
500 try:
501 return info_dict.get("build.prop", {})[prop]
502 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700503 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700504
Doug Zongkerfc44a512014-08-26 13:10:25 -0700505
Tao Baob31892e2017-02-07 11:21:17 -0800506def HandleDowngradeMetadata(metadata):
507 # Only incremental OTAs are allowed to reach here.
508 assert OPTIONS.incremental_source is not None
509
510 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
511 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
512 is_downgrade = long(post_timestamp) < long(pre_timestamp)
513
514 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800515 if not is_downgrade:
516 raise RuntimeError("--downgrade specified but no downgrade detected: "
517 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800518 metadata["ota-downgrade"] = "yes"
519 elif OPTIONS.timestamp:
520 if not is_downgrade:
521 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
522 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
523 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800524 else:
525 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800526 raise RuntimeError("Downgrade detected based on timestamp check: "
527 "pre: %s, post: %s. Need to specify --timestamp OR "
528 "--downgrade to allow building the incremental." % (
529 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800530 metadata["post-timestamp"] = post_timestamp
531
532
Geremy Condra36bd3652014-02-06 19:45:10 -0800533def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
534 source_version = OPTIONS.source_info_dict["recovery_api_version"]
535 target_version = OPTIONS.target_info_dict["recovery_api_version"]
536
537 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700538 print("WARNING: generating edify script for a source that "
539 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700540 script = edify_generator.EdifyGenerator(
541 source_version, OPTIONS.target_info_dict,
542 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800543
Tao Bao3806c232015-07-05 21:08:33 -0700544 recovery_mount_options = OPTIONS.source_info_dict.get(
545 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700546 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
547 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800548 oem_dicts = None
549 if source_oem_props and target_oem_props:
550 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700551
Dan Albert8b72aef2015-03-23 19:13:21 -0700552 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700553 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800554 oem_dicts and oem_dicts[0],
555 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800556 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700557 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800558
Tao Baob31892e2017-02-07 11:21:17 -0800559 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800560
Geremy Condra36bd3652014-02-06 19:45:10 -0800561 device_specific = common.DeviceSpecificParams(
562 source_zip=source_zip,
563 source_version=source_version,
564 target_zip=target_zip,
565 target_version=target_version,
566 output_zip=output_zip,
567 script=script,
568 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700569 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800570
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800571 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700572 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800573 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700574 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800575 metadata["pre-build"] = source_fp
576 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700577 metadata["pre-build-incremental"] = GetBuildProp(
578 "ro.build.version.incremental", OPTIONS.source_info_dict)
579 metadata["post-build-incremental"] = GetBuildProp(
580 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800581
582 source_boot = common.GetBootableImage(
583 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
584 OPTIONS.source_info_dict)
585 target_boot = common.GetBootableImage(
586 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
587 updating_boot = (not OPTIONS.two_step and
588 (source_boot.data != target_boot.data))
589
Geremy Condra36bd3652014-02-06 19:45:10 -0800590 target_recovery = common.GetBootableImage(
591 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800592
Tao Bao7e0f1602017-03-06 15:50:08 -0800593 system_src = GetImage("system", OPTIONS.source_tmp)
594 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700595
596 blockimgdiff_version = 1
597 if OPTIONS.info_dict:
598 blockimgdiff_version = max(
599 int(i) for i in
600 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
601
Tao Baof8acad12016-07-07 09:09:58 -0700602 # Check the first block of the source system partition for remount R/W only
603 # if the filesystem is ext4.
604 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
605 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700606 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
607 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
608 # b) the blocks listed in block map may not contain all the bytes for a given
609 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700610 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
611 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
612 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700613 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800614 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700615 version=blockimgdiff_version,
616 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700617
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700618 if HasVendorPartition(target_zip):
619 if not HasVendorPartition(source_zip):
620 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800621 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
622 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800623
624 # Check first block of vendor partition for remount R/W only if
625 # disk type is ext4
626 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800627 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700628 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700629 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800630 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700631 version=blockimgdiff_version,
632 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700633 else:
634 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800635
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800636 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800637 device_specific.IncrementalOTA_Assertions()
638
639 # Two-step incremental package strategy (in chronological order,
640 # which is *not* the order in which the generated script has
641 # things):
642 #
643 # if stage is not "2/3" or "3/3":
644 # do verification on current system
645 # write recovery image to boot partition
646 # set stage to "2/3"
647 # reboot to boot partition and restart recovery
648 # else if stage is "2/3":
649 # write recovery image to recovery partition
650 # set stage to "3/3"
651 # reboot to recovery partition and restart recovery
652 # else:
653 # (stage must be "3/3")
654 # perform update:
655 # patch system files, etc.
656 # force full install of new boot image
657 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700658 # complete script normally
659 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800660
661 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700662 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800663 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700664 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800665 assert fs.fs_type.upper() == "EMMC", \
666 "two-step packages only supported on devices with EMMC /misc partitions"
667 bcb_dev = {"bcb_dev": fs.device}
668 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
669 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700670if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800671""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800672
673 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
674 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700675 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800676 script.WriteRawImage("/recovery", "recovery.img")
677 script.AppendExtra("""
678set_stage("%(bcb_dev)s", "3/3");
679reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700680else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800681""" % bcb_dev)
682
Tao Baod42e97e2016-11-30 12:11:57 -0800683 # Stage 1/3: (a) Verify the current system.
684 script.Comment("Stage 1/3")
685
Tao Bao6c55a8a2015-04-08 15:30:27 -0700686 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800687 script.Print("Source: %s" % (source_fp,))
688 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700689
Geremy Condra36bd3652014-02-06 19:45:10 -0800690 script.Print("Verifying current system...")
691
692 device_specific.IncrementalOTA_VerifyBegin()
693
Tao Bao3e30d972016-03-15 13:20:19 -0700694 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
695 # patching on a device that's already on the target build will damage the
696 # system. Because operations like move don't check the block state, they
697 # always apply the changes unconditionally.
698 if blockimgdiff_version <= 2:
699 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700700 script.AssertSomeFingerprint(source_fp)
701 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700702 script.AssertSomeThumbprint(
703 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700704
705 else: # blockimgdiff_version > 2
706 if source_oem_props is None and target_oem_props is None:
707 script.AssertSomeFingerprint(source_fp, target_fp)
708 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700709 script.AssertSomeThumbprint(
710 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
711 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700712 elif source_oem_props is None and target_oem_props is not None:
713 script.AssertFingerprintOrThumbprint(
714 source_fp,
715 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
716 else:
717 script.AssertFingerprintOrThumbprint(
718 target_fp,
719 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800720
Tao Baod8d14be2016-02-04 14:26:02 -0800721 # Check the required cache size (i.e. stashed blocks).
722 size = []
723 if system_diff:
724 size.append(system_diff.required_cache)
725 if vendor_diff:
726 size.append(vendor_diff.required_cache)
727
Geremy Condra36bd3652014-02-06 19:45:10 -0800728 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700729 boot_type, boot_device = common.GetTypeAndDevice(
730 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800731 d = common.Difference(target_boot, source_boot)
732 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700733 if d is None:
734 include_full_boot = True
735 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
736 else:
737 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800738
Tao Bao89fbb0f2017-01-10 10:47:58 -0800739 print("boot target: %d source: %d diff: %d" % (
740 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800741
Doug Zongkerf8340082014-08-05 10:39:37 -0700742 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800743
Doug Zongkerf8340082014-08-05 10:39:37 -0700744 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
745 (boot_type, boot_device,
746 source_boot.size, source_boot.sha1,
747 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800748 size.append(target_boot.size)
749
750 if size:
751 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800752
753 device_specific.IncrementalOTA_VerifyEnd()
754
755 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800756 # Stage 1/3: (b) Write recovery image to /boot.
757 _WriteRecoveryImageToBoot(script, output_zip)
758
Geremy Condra36bd3652014-02-06 19:45:10 -0800759 script.AppendExtra("""
760set_stage("%(bcb_dev)s", "2/3");
761reboot_now("%(bcb_dev)s", "");
762else
763""" % bcb_dev)
764
Tao Baod42e97e2016-11-30 12:11:57 -0800765 # Stage 3/3: Make changes.
766 script.Comment("Stage 3/3")
767
Jesse Zhao75bcea02015-01-06 10:59:53 -0800768 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700769 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800770 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700771 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800772
Geremy Condra36bd3652014-02-06 19:45:10 -0800773 script.Comment("---- start making changes here ----")
774
775 device_specific.IncrementalOTA_InstallBegin()
776
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700777 system_diff.WriteScript(script, output_zip,
778 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700779
Doug Zongkerfc44a512014-08-26 13:10:25 -0700780 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700781 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800782
783 if OPTIONS.two_step:
784 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
785 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800786 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800787
788 if not OPTIONS.two_step:
789 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700790 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800791 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700792 script.Print("Installing boot image...")
793 script.WriteRawImage("/boot", "boot.img")
794 else:
795 # Produce the boot image by applying a patch to the current
796 # contents of the boot partition, and write it back to the
797 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -0800798 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700799 script.Print("Patching boot image...")
800 script.ShowProgress(0.1, 10)
801 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
802 % (boot_type, boot_device,
803 source_boot.size, source_boot.sha1,
804 target_boot.size, target_boot.sha1),
805 "-",
806 target_boot.size, target_boot.sha1,
807 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800808 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800809 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800810
811 # Do device-specific installation (eg, write radio image).
812 device_specific.IncrementalOTA_InstallEnd()
813
814 if OPTIONS.extra_script is not None:
815 script.AppendExtra(OPTIONS.extra_script)
816
Doug Zongker922206e2014-03-04 13:16:24 -0800817 if OPTIONS.wipe_user_data:
818 script.Print("Erasing user data...")
819 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -0800820 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -0800821
Geremy Condra36bd3652014-02-06 19:45:10 -0800822 if OPTIONS.two_step:
823 script.AppendExtra("""
824set_stage("%(bcb_dev)s", "");
825endif;
826endif;
827""" % bcb_dev)
828
829 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -0800830 # For downgrade OTAs, we prefer to use the update-binary in the source
831 # build that is actually newer than the one in the target build.
832 if OPTIONS.downgrade:
833 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
834 else:
835 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800836 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -0800837 WriteMetadata(metadata, output_zip)
838
Doug Zongker32b527d2014-03-04 10:03:02 -0800839
Tao Bao9bc6bb22015-11-09 16:58:28 -0800840def WriteVerifyPackage(input_zip, output_zip):
841 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
842
843 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
844 recovery_mount_options = OPTIONS.info_dict.get(
845 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800846 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700847 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800848 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800849
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800850 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
851 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800852 metadata = {
853 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800854 "pre-device": GetOemProperty("ro.product.device", oem_props,
855 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -0800856 OPTIONS.info_dict),
857 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
858 }
859
860 device_specific = common.DeviceSpecificParams(
861 input_zip=input_zip,
862 input_version=OPTIONS.info_dict["recovery_api_version"],
863 output_zip=output_zip,
864 script=script,
865 input_tmp=OPTIONS.input_tmp,
866 metadata=metadata,
867 info_dict=OPTIONS.info_dict)
868
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800869 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800870
871 script.Print("Verifying device images against %s..." % target_fp)
872 script.AppendExtra("")
873
874 script.Print("Verifying boot...")
875 boot_img = common.GetBootableImage(
876 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
877 boot_type, boot_device = common.GetTypeAndDevice(
878 "/boot", OPTIONS.info_dict)
879 script.Verify("%s:%s:%d:%s" % (
880 boot_type, boot_device, boot_img.size, boot_img.sha1))
881 script.AppendExtra("")
882
883 script.Print("Verifying recovery...")
884 recovery_img = common.GetBootableImage(
885 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
886 recovery_type, recovery_device = common.GetTypeAndDevice(
887 "/recovery", OPTIONS.info_dict)
888 script.Verify("%s:%s:%d:%s" % (
889 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
890 script.AppendExtra("")
891
Tao Bao7e0f1602017-03-06 15:50:08 -0800892 system_tgt = GetImage("system", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800893 system_tgt.ResetFileMap()
894 system_diff = common.BlockDifference("system", system_tgt, src=None)
895 system_diff.WriteStrictVerifyScript(script)
896
897 if HasVendorPartition(input_zip):
Tao Bao7e0f1602017-03-06 15:50:08 -0800898 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800899 vendor_tgt.ResetFileMap()
900 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
901 vendor_diff.WriteStrictVerifyScript(script)
902
903 # Device specific partitions, such as radio, bootloader and etc.
904 device_specific.VerifyOTA_Assertions()
905
906 script.SetProgress(1.0)
907 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800908 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800909 WriteMetadata(metadata, output_zip)
910
911
Tao Baoc098e9e2016-01-07 13:03:56 -0800912def WriteABOTAPackageWithBrilloScript(target_file, output_file,
913 source_file=None):
914 """Generate an Android OTA package that has A/B update payload."""
915
Tao Bao2dd1c482017-02-03 16:49:39 -0800916 def ComputeStreamingMetadata(zip_file, reserve_space=False,
917 expected_length=None):
918 """Compute the streaming metadata for a given zip.
919
920 When 'reserve_space' is True, we reserve extra space for the offset and
921 length of the metadata entry itself, although we don't know the final
922 values until the package gets signed. This function will be called again
923 after signing. We then write the actual values and pad the string to the
924 length we set earlier. Note that we can't use the actual length of the
925 metadata entry in the second run. Otherwise the offsets for other entries
926 will be changing again.
927 """
Tao Baoc96316c2017-01-24 22:10:49 -0800928
929 def ComputeEntryOffsetSize(name):
930 """Compute the zip entry offset and size."""
931 info = zip_file.getinfo(name)
932 offset = info.header_offset + len(info.FileHeader())
933 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -0800934 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -0800935
936 # payload.bin and payload_properties.txt must exist.
937 offsets = [ComputeEntryOffsetSize('payload.bin'),
938 ComputeEntryOffsetSize('payload_properties.txt')]
939
940 # care_map.txt is available only if dm-verity is enabled.
941 if 'care_map.txt' in zip_file.namelist():
942 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -0800943
944 # 'META-INF/com/android/metadata' is required. We don't know its actual
945 # offset and length (as well as the values for other entries). So we
946 # reserve 10-byte as a placeholder, which is to cover the space for metadata
947 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
948 # beginning of the zip), as well as the possible value changes in other
949 # entries.
950 if reserve_space:
951 offsets.append('metadata:' + ' ' * 10)
952 else:
953 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
954
955 value = ','.join(offsets)
956 if expected_length is not None:
957 assert len(value) <= expected_length, \
958 'Insufficient reserved space: reserved=%d, actual=%d' % (
959 expected_length, len(value))
960 value += ' ' * (expected_length - len(value))
961 return value
Tao Baoc96316c2017-01-24 22:10:49 -0800962
Alex Deymod8d96ec2016-06-10 16:38:31 -0700963 # The place where the output from the subprocess should go.
964 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
965
Tao Baoc098e9e2016-01-07 13:03:56 -0800966 # Setup signing keys.
967 if OPTIONS.package_key is None:
968 OPTIONS.package_key = OPTIONS.info_dict.get(
969 "default_system_dev_certificate",
970 "build/target/product/security/testkey")
971
Tao Baodea0f8b2016-06-20 17:55:06 -0700972 # A/B updater expects a signing key in RSA format. Gets the key ready for
973 # later use in step 3, unless a payload_signer has been specified.
974 if OPTIONS.payload_signer is None:
975 cmd = ["openssl", "pkcs8",
976 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
977 "-inform", "DER", "-nocrypt"]
978 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
979 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -0700980 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
981 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -0700982 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -0800983
Tao Baodea0f8b2016-06-20 17:55:06 -0700984 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -0800985 temp_zip_file = tempfile.NamedTemporaryFile()
986 output_zip = zipfile.ZipFile(temp_zip_file, "w",
987 compression=zipfile.ZIP_DEFLATED)
988
989 # Metadata to comply with Android OTA package format.
990 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800991 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -0800992 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800993 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -0800994
995 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800996 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -0800997 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -0700998 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
999 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001000 "pre-device": GetOemProperty("ro.product.device", oem_props,
1001 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001002 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001003 "ota-required-cache": "0",
1004 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001005 }
1006
1007 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001008 metadata["pre-build"] = CalculateFingerprint(oem_props,
1009 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001010 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001011 metadata["pre-build-incremental"] = GetBuildProp(
1012 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001013
Tao Baob31892e2017-02-07 11:21:17 -08001014 HandleDowngradeMetadata(metadata)
1015 else:
1016 metadata["post-timestamp"] = GetBuildProp(
1017 "ro.build.date.utc", OPTIONS.info_dict)
1018
Tao Baoc098e9e2016-01-07 13:03:56 -08001019 # 1. Generate payload.
1020 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1021 cmd = ["brillo_update_payload", "generate",
1022 "--payload", payload_file,
1023 "--target_image", target_file]
1024 if source_file is not None:
1025 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001026 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1027 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001028 assert p1.returncode == 0, "brillo_update_payload generate failed"
1029
1030 # 2. Generate hashes of the payload and metadata files.
1031 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1032 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1033 cmd = ["brillo_update_payload", "hash",
1034 "--unsigned_payload", payload_file,
1035 "--signature_size", "256",
1036 "--metadata_hash_file", metadata_sig_file,
1037 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001038 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1039 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001040 assert p1.returncode == 0, "brillo_update_payload hash failed"
1041
1042 # 3. Sign the hashes and insert them back into the payload file.
1043 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1044 suffix=".bin")
1045 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1046 suffix=".bin")
1047 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001048 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001049 cmd = [OPTIONS.payload_signer]
1050 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001051 else:
1052 cmd = ["openssl", "pkeyutl", "-sign",
1053 "-inkey", rsa_key,
1054 "-pkeyopt", "digest:sha256"]
1055 cmd.extend(["-in", payload_sig_file,
1056 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001057 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1058 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001059 assert p1.returncode == 0, "openssl sign payload failed"
1060
1061 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001062 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001063 cmd = [OPTIONS.payload_signer]
1064 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001065 else:
1066 cmd = ["openssl", "pkeyutl", "-sign",
1067 "-inkey", rsa_key,
1068 "-pkeyopt", "digest:sha256"]
1069 cmd.extend(["-in", metadata_sig_file,
1070 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001071 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1072 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001073 assert p1.returncode == 0, "openssl sign metadata failed"
1074
1075 # 3c. Insert the signatures back into the payload file.
1076 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1077 suffix=".bin")
1078 cmd = ["brillo_update_payload", "sign",
1079 "--unsigned_payload", payload_file,
1080 "--payload", signed_payload_file,
1081 "--signature_size", "256",
1082 "--metadata_signature_file", signed_metadata_sig_file,
1083 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001084 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1085 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001086 assert p1.returncode == 0, "brillo_update_payload sign failed"
1087
Alex Deymo19241c12016-02-04 22:29:29 -08001088 # 4. Dump the signed payload properties.
1089 properties_file = common.MakeTempFile(prefix="payload-properties-",
1090 suffix=".txt")
1091 cmd = ["brillo_update_payload", "properties",
1092 "--payload", signed_payload_file,
1093 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001094 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1095 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001096 assert p1.returncode == 0, "brillo_update_payload properties failed"
1097
Tao Bao7c5dc572016-06-14 17:48:11 -07001098 if OPTIONS.wipe_user_data:
1099 with open(properties_file, "a") as f:
1100 f.write("POWERWASH=1\n")
1101 metadata["ota-wipe"] = "yes"
1102
Tao Baoc96316c2017-01-24 22:10:49 -08001103 # Add the signed payload file and properties into the zip. In order to
1104 # support streaming, we pack payload.bin, payload_properties.txt and
1105 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1106 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001107 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1108 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001109 common.ZipWrite(output_zip, properties_file,
1110 arcname="payload_properties.txt",
1111 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001112
Tianjie Xucfa86222016-03-07 16:31:19 -08001113 # If dm-verity is supported for the device, copy contents of care_map
1114 # into A/B OTA package.
1115 if OPTIONS.info_dict.get("verity") == "true":
1116 target_zip = zipfile.ZipFile(target_file, "r")
1117 care_map_path = "META/care_map.txt"
1118 namelist = target_zip.namelist()
1119 if care_map_path in namelist:
1120 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001121 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1122 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001123 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001124 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001125 common.ZipClose(target_zip)
1126
Tao Bao2dd1c482017-02-03 16:49:39 -08001127 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001128 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001129 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001130 WriteMetadata(metadata, output_zip)
1131 common.ZipClose(output_zip)
1132
Tao Bao2dd1c482017-02-03 16:49:39 -08001133 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1134 # zip entries, as well as padding the entry headers. We do a preliminary
1135 # signing (with an incomplete metadata entry) to allow that to happen. Then
1136 # compute the zip entry offsets, write back the final metadata and do the
1137 # final signing.
1138 prelim_signing = tempfile.NamedTemporaryFile()
1139 SignOutput(temp_zip_file.name, prelim_signing.name)
1140 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001141
Tao Bao2dd1c482017-02-03 16:49:39 -08001142 # Open the signed zip. Compute the final metadata that's needed for streaming.
1143 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1144 compression=zipfile.ZIP_DEFLATED)
1145 expected_length = len(metadata['ota-streaming-property-files'])
1146 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1147 prelim_zip, reserve_space=False, expected_length=expected_length)
1148
1149 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1150 final_signing = tempfile.NamedTemporaryFile()
1151 output_zip = zipfile.ZipFile(final_signing, "w",
1152 compression=zipfile.ZIP_DEFLATED)
1153 for item in prelim_zip.infolist():
1154 if item.filename == METADATA_NAME:
1155 continue
1156
1157 data = prelim_zip.read(item.filename)
1158 out_info = copy.copy(item)
1159 common.ZipWriteStr(output_zip, out_info, data)
1160
1161 # Now write the final metadata entry.
1162 WriteMetadata(metadata, output_zip)
1163 common.ZipClose(prelim_zip)
1164 common.ZipClose(output_zip)
1165
1166 # Re-sign the package after updating the metadata entry.
1167 SignOutput(final_signing.name, output_file)
1168 final_signing.close()
1169
1170 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001171 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001172 actual = metadata['ota-streaming-property-files'].strip()
1173 expected = ComputeStreamingMetadata(output_zip)
1174 assert actual == expected, \
1175 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001176 common.ZipClose(output_zip)
1177
Tao Baoc098e9e2016-01-07 13:03:56 -08001178
Doug Zongkereef39442009-04-02 12:14:19 -07001179def main(argv):
1180
1181 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001182 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001183 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001184 elif o in ("-k", "--package_key"):
1185 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001186 elif o in ("-i", "--incremental_from"):
1187 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001188 elif o == "--full_radio":
1189 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001190 elif o == "--full_bootloader":
1191 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001192 elif o in ("-w", "--wipe_user_data"):
1193 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001194 elif o == "--downgrade":
1195 OPTIONS.downgrade = True
1196 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001197 elif o == "--override_timestamp":
1198 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001199 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001200 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001201 elif o == "--oem_no_mount":
1202 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001203 elif o in ("-e", "--extra_script"):
1204 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001205 elif o in ("-t", "--worker_threads"):
1206 if a.isdigit():
1207 OPTIONS.worker_threads = int(a)
1208 else:
1209 raise ValueError("Cannot parse value %r for option %r - only "
1210 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001211 elif o in ("-2", "--two_step"):
1212 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001213 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001214 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001215 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001216 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001217 elif o == "--block":
1218 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001219 elif o in ("-b", "--binary"):
1220 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001221 elif o in ("--no_fallback_to_full",):
1222 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001223 elif o == "--stash_threshold":
1224 try:
1225 OPTIONS.stash_threshold = float(a)
1226 except ValueError:
1227 raise ValueError("Cannot parse value %r for option %r - expecting "
1228 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001229 elif o == "--gen_verify":
1230 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001231 elif o == "--log_diff":
1232 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001233 elif o == "--payload_signer":
1234 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001235 elif o == "--payload_signer_args":
1236 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07001237 else:
1238 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001239 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001240
1241 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08001242 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001243 extra_long_opts=[
1244 "board_config=",
1245 "package_key=",
1246 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001247 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001248 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001249 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001250 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001251 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001252 "extra_script=",
1253 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001254 "two_step",
1255 "no_signing",
1256 "block",
1257 "binary=",
1258 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001259 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001260 "verify",
1261 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001262 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001263 "gen_verify",
1264 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001265 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001266 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001267 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001268
1269 if len(args) != 2:
1270 common.Usage(__doc__)
1271 sys.exit(1)
1272
Tao Bao5d182562016-02-23 11:38:39 -08001273 if OPTIONS.downgrade:
1274 # Sanity check to enforce a data wipe.
1275 if not OPTIONS.wipe_user_data:
1276 raise ValueError("Cannot downgrade without a data wipe")
1277
1278 # We should only allow downgrading incrementals (as opposed to full).
1279 # Otherwise the device may go back from arbitrary build with this full
1280 # OTA package.
1281 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001282 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001283
Tao Bao3e6161a2017-02-28 11:48:48 -08001284 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1285 "Cannot have --downgrade AND --override_timestamp both"
1286
Tao Baoc098e9e2016-01-07 13:03:56 -08001287 # Load the dict file from the zip directly to have a peek at the OTA type.
1288 # For packages using A/B update, unzipping is not needed.
1289 input_zip = zipfile.ZipFile(args[0], "r")
1290 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1291 common.ZipClose(input_zip)
1292
1293 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1294
1295 if ab_update:
1296 if OPTIONS.incremental_source is not None:
1297 OPTIONS.target_info_dict = OPTIONS.info_dict
1298 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1299 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1300 common.ZipClose(source_zip)
1301
1302 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001303 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001304 common.DumpInfoDict(OPTIONS.info_dict)
1305
1306 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001307 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001308 common.DumpInfoDict(OPTIONS.source_info_dict)
1309
1310 WriteABOTAPackageWithBrilloScript(
1311 target_file=args[0],
1312 output_file=args[1],
1313 source_file=OPTIONS.incremental_source)
1314
Tao Bao89fbb0f2017-01-10 10:47:58 -08001315 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001316 return
1317
Doug Zongker1c390a22009-05-14 19:06:36 -07001318 if OPTIONS.extra_script is not None:
1319 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1320
Tao Bao89fbb0f2017-01-10 10:47:58 -08001321 print("unzipping target target-files...")
Tao Bao6b0b2f92017-03-05 11:38:11 -08001322 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
Tao Bao457cbf62017-03-06 09:56:01 -08001323 args[0], UNZIP_PATTERN)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001324
Doug Zongkereef39442009-04-02 12:14:19 -07001325 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001326 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001327
Doug Zongker37974732010-09-16 17:44:38 -07001328 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001329 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07001330 common.DumpInfoDict(OPTIONS.info_dict)
1331
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001332 # If the caller explicitly specified the device-specific extensions
1333 # path via -s/--device_specific, use that. Otherwise, use
1334 # META/releasetools.py if it is present in the target target_files.
1335 # Otherwise, take the path of the file from 'tool_extensions' in the
1336 # info dict and look for that in the local filesystem, relative to
1337 # the current directory.
1338
Doug Zongker37974732010-09-16 17:44:38 -07001339 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001340 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1341 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001342 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001343 OPTIONS.device_specific = from_input
1344 else:
1345 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1346
Doug Zongker37974732010-09-16 17:44:38 -07001347 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001348 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001349
Tao Baoc098e9e2016-01-07 13:03:56 -08001350 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001351 raise common.ExternalError(
1352 "--- target build has specified no recovery ---")
1353
Tao Bao767e3ac2015-11-10 12:19:19 -08001354 # Use the default key to sign the package if not specified with package_key.
1355 if not OPTIONS.no_signing:
1356 if OPTIONS.package_key is None:
1357 OPTIONS.package_key = OPTIONS.info_dict.get(
1358 "default_system_dev_certificate",
1359 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001360
Tao Bao767e3ac2015-11-10 12:19:19 -08001361 # Set up the output zip. Create a temporary zip file if signing is needed.
1362 if OPTIONS.no_signing:
1363 if os.path.exists(args[1]):
1364 os.unlink(args[1])
1365 output_zip = zipfile.ZipFile(args[1], "w",
1366 compression=zipfile.ZIP_DEFLATED)
1367 else:
1368 temp_zip_file = tempfile.NamedTemporaryFile()
1369 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1370 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001371
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001372 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001373 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001374 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001375 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001376 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001377
Tao Bao9bc6bb22015-11-09 16:58:28 -08001378 # Generate a verify package.
1379 if OPTIONS.gen_verify:
1380 WriteVerifyPackage(input_zip, output_zip)
1381
Tao Bao767e3ac2015-11-10 12:19:19 -08001382 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001383 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001384 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001385
1386 # Generate an incremental OTA. It will fall back to generate a full OTA on
1387 # failure unless no_fallback_to_full is specified.
1388 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001389 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001390 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001391 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001392 UNZIP_PATTERN)
Tao Bao767e3ac2015-11-10 12:19:19 -08001393 OPTIONS.target_info_dict = OPTIONS.info_dict
1394 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1395 OPTIONS.source_tmp)
1396 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001397 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001398 common.DumpInfoDict(OPTIONS.source_info_dict)
1399 try:
Tao Bao457cbf62017-03-06 09:56:01 -08001400 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001401 if OPTIONS.log_diff:
1402 out_file = open(OPTIONS.log_diff, 'w')
1403 import target_files_diff
1404 target_files_diff.recursiveDiff('',
1405 OPTIONS.source_tmp,
1406 OPTIONS.input_tmp,
1407 out_file)
1408 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001409 except ValueError:
1410 if not OPTIONS.fallback_to_full:
1411 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08001412 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001413 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001414 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001415
Tao Bao767e3ac2015-11-10 12:19:19 -08001416 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001417
Tao Bao767e3ac2015-11-10 12:19:19 -08001418 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001419 if not OPTIONS.no_signing:
1420 SignOutput(temp_zip_file.name, args[1])
1421 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001422
Tao Bao89fbb0f2017-01-10 10:47:58 -08001423 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001424
1425
1426if __name__ == '__main__':
1427 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001428 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001429 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001430 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001431 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001432 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001433 finally:
1434 common.Cleanup()