blob: f75bb969c0fe979387cb9e02e75bf6bc9d174d30 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080053 -o (--oem_settings) <main_file[,additional_files...]>
54 Comma seperated list of files used to specify the expected OEM-specific
55 properties on the OEM partition of the intended device.
56 Multiple expected values can be used by providing multiple files.
57
Tao Bao8608cde2016-02-25 19:49:55 -080058 --oem_no_mount
59 For devices with OEM-specific properties but without an OEM partition,
60 do not mount the OEM partition in the updater-script. This should be
61 very rarely used, since it's expected to have a dedicated OEM partition
62 for OEM-specific properties. Only meaningful when -o is specified.
63
Doug Zongkerdbfaae52009-04-21 17:12:54 -070064 -w (--wipe_user_data)
65 Generate an OTA package that will wipe the user data partition
66 when installed.
67
Tao Bao5d182562016-02-23 11:38:39 -080068 --downgrade
69 Intentionally generate an incremental OTA that updates from a newer
70 build to an older one (based on timestamp comparison). "post-timestamp"
71 will be replaced by "ota-downgrade=yes" in the metadata file. A data
72 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080073 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080074 the OTA package, unless --binary flag is specified. Please also check the
75 doc for --override_timestamp below.
76
77 --override_timestamp
78 Intentionally generate an incremental OTA that updates from a newer
79 build to an older one (based on timestamp comparison), by overriding the
80 timestamp in package metadata. This differs from --downgrade flag: we
81 know for sure this is NOT an actual downgrade case, but two builds are
82 cut in a reverse order. A legit use case is that we cut a new build C
83 (after having A and B), but want to enfore an update path of A -> C -> B.
84 Specifying --downgrade may not help since that would enforce a data wipe
85 for C -> B update. The value of "post-timestamp" will be set to the newer
86 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080087
Doug Zongker1c390a22009-05-14 19:06:36 -070088 -e (--extra_script) <file>
89 Insert the contents of file at the end of the update script.
90
Doug Zongker9b23f2c2013-11-25 14:44:12 -080091 -2 (--two_step)
92 Generate a 'two-step' OTA package, where recovery is updated
93 first, so that any changes made to the system partition are done
94 using the new recovery (new kernel, etc.).
95
Doug Zongker26e66192014-02-20 13:22:07 -080096 --block
Tao Bao457cbf62017-03-06 09:56:01 -080097 Generate a block-based OTA for non-A/B device. We have deprecated the
98 support for file-based OTA since O. Block-based OTA will be used by
99 default for all non-A/B devices. Keeping this flag here to not break
100 existing callers.
Doug Zongker26e66192014-02-20 13:22:07 -0800101
Doug Zongker25568482014-03-03 10:21:27 -0800102 -b (--binary) <file>
103 Use the given binary as the update-binary in the output package,
104 instead of the binary in the build's target_files. Use for
105 development only.
106
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200107 -t (--worker_threads) <int>
108 Specifies the number of worker-threads that will be used when
109 generating patches for incremental updates (defaults to 3).
110
Tao Bao8dcf7382015-05-21 14:09:49 -0700111 --stash_threshold <float>
112 Specifies the threshold that will be used to compute the maximum
113 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800114
115 --gen_verify
116 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800117
118 --log_diff <file>
119 Generate a log file that shows the differences in the source and target
120 builds for an incremental package. This option is only meaningful when
121 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700122
123 --payload_signer <signer>
124 Specify the signer when signing the payload and metadata for A/B OTAs.
125 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
126 with the package private key. If the private key cannot be accessed
127 directly, a payload signer that knows how to do that should be specified.
128 The signer will be supplied with "-inkey <path_to_key>",
129 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700130
131 --payload_signer_args <args>
132 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700133"""
134
Tao Bao89fbb0f2017-01-10 10:47:58 -0800135from __future__ import print_function
136
Doug Zongkereef39442009-04-02 12:14:19 -0700137import sys
138
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800139if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800140 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700141 sys.exit(1)
142
Tao Bao2dd1c482017-02-03 16:49:39 -0800143import copy
Doug Zongkerfc44a512014-08-26 13:10:25 -0700144import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800145import os.path
Tao Baoc098e9e2016-01-07 13:03:56 -0800146import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700147import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700148import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700149import zipfile
150
151import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700152import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700153import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700154
155OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700156OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700157OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700158OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700159OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700160OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800161OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800162OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700163OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700164OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
165if OPTIONS.worker_threads == 0:
166 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800167OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900168OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800169OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800170OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700171OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800172OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700173OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700174OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700175OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700176# Stash size cannot exceed cache_size * threshold.
177OPTIONS.cache_size = None
178OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800179OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800180OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700181OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700182OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700183OPTIONS.extracted_input = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700184
Tao Bao2dd1c482017-02-03 16:49:39 -0800185METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800186UNZIP_PATTERN = ['IMAGES/*', 'META/*']
187
Tao Bao2dd1c482017-02-03 16:49:39 -0800188
Doug Zongkereef39442009-04-02 12:14:19 -0700189def SignOutput(temp_zip_name, output_zip_name):
190 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
191 pw = key_passwords[OPTIONS.package_key]
192
Doug Zongker951495f2009-08-14 12:44:19 -0700193 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
194 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700195
196
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800197def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700198 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700199 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700200 device = GetBuildProp("ro.product.device", info_dict)
201 script.AssertDevice(device)
202 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800203 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700204 raise common.ExternalError(
205 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700206 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800207 values = []
208 for oem_dict in oem_dicts:
209 if oem_dict.get(prop):
210 values.append(oem_dict[prop])
211 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700212 raise common.ExternalError(
213 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800214 script.AssertOemProperty(prop, values)
215
216
Tao Baoebce6972017-03-06 10:22:20 -0800217def _LoadOemDicts(script, recovery_mount_options=None):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800218 """Returns the list of loaded OEM properties dict."""
219 oem_dicts = None
220 if OPTIONS.oem_source is None:
221 raise common.ExternalError("OEM source required for this build")
Tao Baoebce6972017-03-06 10:22:20 -0800222 if not OPTIONS.oem_no_mount and script:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800223 script.Mount("/oem", recovery_mount_options)
224 oem_dicts = []
225 for oem_file in OPTIONS.oem_source:
226 oem_dicts.append(common.LoadDictionaryFromLines(
227 open(oem_file).readlines()))
228 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700229
Doug Zongkereef39442009-04-02 12:14:19 -0700230
Tao Baod42e97e2016-11-30 12:11:57 -0800231def _WriteRecoveryImageToBoot(script, output_zip):
232 """Find and write recovery image to /boot in two-step OTA.
233
234 In two-step OTAs, we write recovery image to /boot as the first step so that
235 we can reboot to there and install a new recovery image to /recovery.
236 A special "recovery-two-step.img" will be preferred, which encodes the correct
237 path of "/boot". Otherwise the device may show "device is corrupt" message
238 when booting into /boot.
239
240 Fall back to using the regular recovery.img if the two-step recovery image
241 doesn't exist. Note that rebuilding the special image at this point may be
242 infeasible, because we don't have the desired boot signer and keys when
243 calling ota_from_target_files.py.
244 """
245
246 recovery_two_step_img_name = "recovery-two-step.img"
247 recovery_two_step_img_path = os.path.join(
248 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
249 if os.path.exists(recovery_two_step_img_path):
250 recovery_two_step_img = common.GetBootableImage(
251 recovery_two_step_img_name, recovery_two_step_img_name,
252 OPTIONS.input_tmp, "RECOVERY")
253 common.ZipWriteStr(
254 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800255 print("two-step package: using %s in stage 1/3" % (
256 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800257 script.WriteRawImage("/boot", recovery_two_step_img_name)
258 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800259 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800260 # The "recovery.img" entry has been written into package earlier.
261 script.WriteRawImage("/boot", "recovery.img")
262
263
Doug Zongkerc9253822014-02-04 12:17:58 -0800264def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700265 namelist = [name for name in target_files_zip.namelist()]
266 return ("SYSTEM/recovery-from-boot.p" in namelist or
267 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700268
Tao Bao457cbf62017-03-06 09:56:01 -0800269
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700270def HasVendorPartition(target_files_zip):
271 try:
272 target_files_zip.getinfo("VENDOR/")
273 return True
274 except KeyError:
275 return False
276
Tao Bao457cbf62017-03-06 09:56:01 -0800277
Michael Runge6e836112014-04-15 17:40:21 -0700278def GetOemProperty(name, oem_props, oem_dict, info_dict):
279 if oem_props is not None and name in oem_props:
280 return oem_dict[name]
281 return GetBuildProp(name, info_dict)
282
283
284def CalculateFingerprint(oem_props, oem_dict, info_dict):
285 if oem_props is None:
286 return GetBuildProp("ro.build.fingerprint", info_dict)
287 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700288 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
289 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
290 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
291 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700292
Doug Zongkerfc44a512014-08-26 13:10:25 -0700293
Tao Bao7e0f1602017-03-06 15:50:08 -0800294def GetImage(which, tmpdir):
295 """Returns an image object suitable for passing to BlockImageDiff.
296
297 'which' partition must be "system" or "vendor". A prebuilt image and file
298 map must already exist in tmpdir.
299 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700300
301 assert which in ("system", "vendor")
302
303 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700304 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700305
Tao Bao7e0f1602017-03-06 15:50:08 -0800306 # The image and map files must have been created prior to calling
307 # ota_from_target_files.py (since LMP).
308 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700309
Tao Baoff777812015-05-12 11:42:31 -0700310 # Bug: http://b/20939131
311 # In ext4 filesystems, block 0 might be changed even being mounted
312 # R/O. We add it to clobbered_blocks so that it will be written to the
313 # target unconditionally. Note that they are still part of care_map.
314 clobbered_blocks = "0"
315
316 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700317
318
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700319def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700320 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700321 # be installed on top of. For now, we expect the API just won't
322 # change very often. Similarly for fstab, it might have changed
323 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700324 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700325
Tao Bao838c68f2016-03-15 19:16:18 +0000326 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700327 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800328 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700329 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800330 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700331
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800332 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
333 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700334 metadata = {
Tao Bao39f3eaf2017-03-09 15:01:11 -0800335 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800336 "pre-device": GetOemProperty("ro.product.device", oem_props,
337 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700338 OPTIONS.info_dict),
339 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
340 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700341
Doug Zongker05d3dea2009-06-22 11:32:31 -0700342 device_specific = common.DeviceSpecificParams(
343 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700344 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700345 output_zip=output_zip,
346 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700347 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700348 metadata=metadata,
349 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700350
Tao Bao457cbf62017-03-06 09:56:01 -0800351 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800352
Tao Bao457cbf62017-03-06 09:56:01 -0800353 metadata["ota-type"] = "BLOCK"
Tao Baod8d14be2016-02-04 14:26:02 -0800354
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700355 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
356 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
357 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700358
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800359 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700360 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800361
362 # Two-step package strategy (in chronological order, which is *not*
363 # the order in which the generated script has things):
364 #
365 # if stage is not "2/3" or "3/3":
366 # write recovery image to boot partition
367 # set stage to "2/3"
368 # reboot to boot partition and restart recovery
369 # else if stage is "2/3":
370 # write recovery image to recovery partition
371 # set stage to "3/3"
372 # reboot to recovery partition and restart recovery
373 # else:
374 # (stage must be "3/3")
375 # set stage to ""
376 # do normal full package installation:
377 # wipe and install system, boot image, etc.
378 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700379 # complete script normally
380 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800381
382 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
383 OPTIONS.input_tmp, "RECOVERY")
384 if OPTIONS.two_step:
385 if not OPTIONS.info_dict.get("multistage_support", None):
386 assert False, "two-step packages not supported by this build"
387 fs = OPTIONS.info_dict["fstab"]["/misc"]
388 assert fs.fs_type.upper() == "EMMC", \
389 "two-step packages only supported on devices with EMMC /misc partitions"
390 bcb_dev = {"bcb_dev": fs.device}
391 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
392 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700393if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800394""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800395
396 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
397 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800398 script.WriteRawImage("/recovery", "recovery.img")
399 script.AppendExtra("""
400set_stage("%(bcb_dev)s", "3/3");
401reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700402else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800403""" % bcb_dev)
404
Tao Baod42e97e2016-11-30 12:11:57 -0800405 # Stage 3/3: Make changes.
406 script.Comment("Stage 3/3")
407
Tao Bao6c55a8a2015-04-08 15:30:27 -0700408 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700409 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700410
Doug Zongkere5ff5902012-01-17 10:55:37 -0800411 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700412
Doug Zongker01ce19c2014-02-04 13:48:15 -0800413 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700414
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700415 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800416 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700417 if HasVendorPartition(input_zip):
418 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700419
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400420 # Place a copy of file_contexts.bin into the OTA package which will be used
421 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700422 if "selinux_fc" in OPTIONS.info_dict:
423 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500424
Michael Runge7cd99ba2014-10-22 17:21:48 -0700425 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
426
Doug Zongker4b9596f2014-06-09 14:15:45 -0700427 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800428
Tao Bao457cbf62017-03-06 09:56:01 -0800429 # Full OTA is done as an "incremental" against an empty source image. This
430 # has the effect of writing new data from the package to the entire
431 # partition, but lets us reuse the updater code that writes incrementals to
432 # do it.
433 system_tgt = GetImage("system", OPTIONS.input_tmp)
434 system_tgt.ResetFileMap()
435 system_diff = common.BlockDifference("system", system_tgt, src=None)
436 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700437
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700438 boot_img = common.GetBootableImage(
439 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800440
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700441 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700442 script.ShowProgress(0.1, 0)
443
Tao Bao457cbf62017-03-06 09:56:01 -0800444 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
445 vendor_tgt.ResetFileMap()
446 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
447 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700448
Doug Zongker37974732010-09-16 17:44:38 -0700449 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700450 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700451
Doug Zongker01ce19c2014-02-04 13:48:15 -0800452 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700453 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700454
Doug Zongker01ce19c2014-02-04 13:48:15 -0800455 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700456 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700457
Doug Zongker1c390a22009-05-14 19:06:36 -0700458 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700459 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700460
Doug Zongker14833602010-02-02 13:12:04 -0800461 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800462
Doug Zongker922206e2014-03-04 13:16:24 -0800463 if OPTIONS.wipe_user_data:
464 script.ShowProgress(0.1, 10)
465 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700466
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800467 if OPTIONS.two_step:
468 script.AppendExtra("""
469set_stage("%(bcb_dev)s", "");
470""" % bcb_dev)
471 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800472
473 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
474 script.Comment("Stage 1/3")
475 _WriteRecoveryImageToBoot(script, output_zip)
476
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800477 script.AppendExtra("""
478set_stage("%(bcb_dev)s", "2/3");
479reboot_now("%(bcb_dev)s", "");
480endif;
481endif;
482""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800483
Tao Bao5d182562016-02-23 11:38:39 -0800484 script.SetProgress(1)
485 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800486 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700487 WriteMetadata(metadata, output_zip)
488
Doug Zongkerfc44a512014-08-26 13:10:25 -0700489
Dan Albert8e0178d2015-01-27 15:53:15 -0800490def WritePolicyConfig(file_name, output_zip):
491 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500492
Doug Zongker2ea21062010-04-28 16:05:21 -0700493
494def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800495 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
496 common.ZipWriteStr(output_zip, METADATA_NAME, value,
497 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700498
Doug Zongkerfc44a512014-08-26 13:10:25 -0700499
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700500def GetBuildProp(prop, info_dict):
501 """Return the fingerprint of the build of a given target-files info_dict."""
502 try:
503 return info_dict.get("build.prop", {})[prop]
504 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700505 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700506
Doug Zongkerfc44a512014-08-26 13:10:25 -0700507
Tao Baob31892e2017-02-07 11:21:17 -0800508def HandleDowngradeMetadata(metadata):
509 # Only incremental OTAs are allowed to reach here.
510 assert OPTIONS.incremental_source is not None
511
512 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
513 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
514 is_downgrade = long(post_timestamp) < long(pre_timestamp)
515
516 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800517 if not is_downgrade:
518 raise RuntimeError("--downgrade specified but no downgrade detected: "
519 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800520 metadata["ota-downgrade"] = "yes"
521 elif OPTIONS.timestamp:
522 if not is_downgrade:
523 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
524 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
525 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800526 else:
527 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800528 raise RuntimeError("Downgrade detected based on timestamp check: "
529 "pre: %s, post: %s. Need to specify --timestamp OR "
530 "--downgrade to allow building the incremental." % (
531 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800532 metadata["post-timestamp"] = post_timestamp
533
534
Geremy Condra36bd3652014-02-06 19:45:10 -0800535def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
536 source_version = OPTIONS.source_info_dict["recovery_api_version"]
537 target_version = OPTIONS.target_info_dict["recovery_api_version"]
538
539 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700540 print("WARNING: generating edify script for a source that "
541 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700542 script = edify_generator.EdifyGenerator(
543 source_version, OPTIONS.target_info_dict,
544 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800545
Tao Bao3806c232015-07-05 21:08:33 -0700546 recovery_mount_options = OPTIONS.source_info_dict.get(
547 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700548 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
549 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800550 oem_dicts = None
551 if source_oem_props and target_oem_props:
552 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700553
Dan Albert8b72aef2015-03-23 19:13:21 -0700554 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700555 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800556 oem_dicts and oem_dicts[0],
557 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800558 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700559 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800560
Tao Baob31892e2017-02-07 11:21:17 -0800561 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800562
Geremy Condra36bd3652014-02-06 19:45:10 -0800563 device_specific = common.DeviceSpecificParams(
564 source_zip=source_zip,
565 source_version=source_version,
566 target_zip=target_zip,
567 target_version=target_version,
568 output_zip=output_zip,
569 script=script,
570 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700571 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800572
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800573 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700574 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800575 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700576 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800577 metadata["pre-build"] = source_fp
578 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700579 metadata["pre-build-incremental"] = GetBuildProp(
580 "ro.build.version.incremental", OPTIONS.source_info_dict)
581 metadata["post-build-incremental"] = GetBuildProp(
582 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800583
584 source_boot = common.GetBootableImage(
585 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
586 OPTIONS.source_info_dict)
587 target_boot = common.GetBootableImage(
588 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
589 updating_boot = (not OPTIONS.two_step and
590 (source_boot.data != target_boot.data))
591
Geremy Condra36bd3652014-02-06 19:45:10 -0800592 target_recovery = common.GetBootableImage(
593 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800594
Tao Bao7e0f1602017-03-06 15:50:08 -0800595 system_src = GetImage("system", OPTIONS.source_tmp)
596 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700597
598 blockimgdiff_version = 1
599 if OPTIONS.info_dict:
600 blockimgdiff_version = max(
601 int(i) for i in
602 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
603
Tao Baof8acad12016-07-07 09:09:58 -0700604 # Check the first block of the source system partition for remount R/W only
605 # if the filesystem is ext4.
606 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
607 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700608 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
609 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
610 # b) the blocks listed in block map may not contain all the bytes for a given
611 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700612 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
613 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
614 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700615 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800616 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700617 version=blockimgdiff_version,
618 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700619
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700620 if HasVendorPartition(target_zip):
621 if not HasVendorPartition(source_zip):
622 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800623 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
624 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800625
626 # Check first block of vendor partition for remount R/W only if
627 # disk type is ext4
628 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800629 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700630 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700631 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800632 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700633 version=blockimgdiff_version,
634 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700635 else:
636 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800637
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800638 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800639 device_specific.IncrementalOTA_Assertions()
640
641 # Two-step incremental package strategy (in chronological order,
642 # which is *not* the order in which the generated script has
643 # things):
644 #
645 # if stage is not "2/3" or "3/3":
646 # do verification on current system
647 # write recovery image to boot partition
648 # set stage to "2/3"
649 # reboot to boot partition and restart recovery
650 # else if stage is "2/3":
651 # write recovery image to recovery partition
652 # set stage to "3/3"
653 # reboot to recovery partition and restart recovery
654 # else:
655 # (stage must be "3/3")
656 # perform update:
657 # patch system files, etc.
658 # force full install of new boot image
659 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700660 # complete script normally
661 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800662
663 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700664 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800665 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700666 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800667 assert fs.fs_type.upper() == "EMMC", \
668 "two-step packages only supported on devices with EMMC /misc partitions"
669 bcb_dev = {"bcb_dev": fs.device}
670 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
671 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700672if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800673""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800674
675 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
676 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700677 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800678 script.WriteRawImage("/recovery", "recovery.img")
679 script.AppendExtra("""
680set_stage("%(bcb_dev)s", "3/3");
681reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700682else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800683""" % bcb_dev)
684
Tao Baod42e97e2016-11-30 12:11:57 -0800685 # Stage 1/3: (a) Verify the current system.
686 script.Comment("Stage 1/3")
687
Tao Bao6c55a8a2015-04-08 15:30:27 -0700688 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800689 script.Print("Source: %s" % (source_fp,))
690 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700691
Geremy Condra36bd3652014-02-06 19:45:10 -0800692 script.Print("Verifying current system...")
693
694 device_specific.IncrementalOTA_VerifyBegin()
695
Tao Bao3e30d972016-03-15 13:20:19 -0700696 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
697 # patching on a device that's already on the target build will damage the
698 # system. Because operations like move don't check the block state, they
699 # always apply the changes unconditionally.
700 if blockimgdiff_version <= 2:
701 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700702 script.AssertSomeFingerprint(source_fp)
703 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700704 script.AssertSomeThumbprint(
705 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700706
707 else: # blockimgdiff_version > 2
708 if source_oem_props is None and target_oem_props is None:
709 script.AssertSomeFingerprint(source_fp, target_fp)
710 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700711 script.AssertSomeThumbprint(
712 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
713 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700714 elif source_oem_props is None and target_oem_props is not None:
715 script.AssertFingerprintOrThumbprint(
716 source_fp,
717 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
718 else:
719 script.AssertFingerprintOrThumbprint(
720 target_fp,
721 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800722
Tao Baod8d14be2016-02-04 14:26:02 -0800723 # Check the required cache size (i.e. stashed blocks).
724 size = []
725 if system_diff:
726 size.append(system_diff.required_cache)
727 if vendor_diff:
728 size.append(vendor_diff.required_cache)
729
Geremy Condra36bd3652014-02-06 19:45:10 -0800730 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700731 boot_type, boot_device = common.GetTypeAndDevice(
732 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800733 d = common.Difference(target_boot, source_boot)
734 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700735 if d is None:
736 include_full_boot = True
737 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
738 else:
739 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800740
Tao Bao89fbb0f2017-01-10 10:47:58 -0800741 print("boot target: %d source: %d diff: %d" % (
742 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800743
Doug Zongkerf8340082014-08-05 10:39:37 -0700744 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800745
Doug Zongkerf8340082014-08-05 10:39:37 -0700746 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
747 (boot_type, boot_device,
748 source_boot.size, source_boot.sha1,
749 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800750 size.append(target_boot.size)
751
752 if size:
753 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800754
755 device_specific.IncrementalOTA_VerifyEnd()
756
757 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800758 # Stage 1/3: (b) Write recovery image to /boot.
759 _WriteRecoveryImageToBoot(script, output_zip)
760
Geremy Condra36bd3652014-02-06 19:45:10 -0800761 script.AppendExtra("""
762set_stage("%(bcb_dev)s", "2/3");
763reboot_now("%(bcb_dev)s", "");
764else
765""" % bcb_dev)
766
Tao Baod42e97e2016-11-30 12:11:57 -0800767 # Stage 3/3: Make changes.
768 script.Comment("Stage 3/3")
769
Jesse Zhao75bcea02015-01-06 10:59:53 -0800770 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700771 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800772 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700773 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800774
Geremy Condra36bd3652014-02-06 19:45:10 -0800775 script.Comment("---- start making changes here ----")
776
777 device_specific.IncrementalOTA_InstallBegin()
778
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700779 system_diff.WriteScript(script, output_zip,
780 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700781
Doug Zongkerfc44a512014-08-26 13:10:25 -0700782 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700783 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800784
785 if OPTIONS.two_step:
786 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
787 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800788 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800789
790 if not OPTIONS.two_step:
791 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700792 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800793 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700794 script.Print("Installing boot image...")
795 script.WriteRawImage("/boot", "boot.img")
796 else:
797 # Produce the boot image by applying a patch to the current
798 # contents of the boot partition, and write it back to the
799 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -0800800 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700801 script.Print("Patching boot image...")
802 script.ShowProgress(0.1, 10)
803 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
804 % (boot_type, boot_device,
805 source_boot.size, source_boot.sha1,
806 target_boot.size, target_boot.sha1),
807 "-",
808 target_boot.size, target_boot.sha1,
809 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800810 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800811 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800812
813 # Do device-specific installation (eg, write radio image).
814 device_specific.IncrementalOTA_InstallEnd()
815
816 if OPTIONS.extra_script is not None:
817 script.AppendExtra(OPTIONS.extra_script)
818
Doug Zongker922206e2014-03-04 13:16:24 -0800819 if OPTIONS.wipe_user_data:
820 script.Print("Erasing user data...")
821 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -0800822 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -0800823
Geremy Condra36bd3652014-02-06 19:45:10 -0800824 if OPTIONS.two_step:
825 script.AppendExtra("""
826set_stage("%(bcb_dev)s", "");
827endif;
828endif;
829""" % bcb_dev)
830
831 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -0800832 # For downgrade OTAs, we prefer to use the update-binary in the source
833 # build that is actually newer than the one in the target build.
834 if OPTIONS.downgrade:
835 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
836 else:
837 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800838 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -0800839 WriteMetadata(metadata, output_zip)
840
Doug Zongker32b527d2014-03-04 10:03:02 -0800841
Tao Bao9bc6bb22015-11-09 16:58:28 -0800842def WriteVerifyPackage(input_zip, output_zip):
843 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
844
845 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
846 recovery_mount_options = OPTIONS.info_dict.get(
847 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800848 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700849 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800850 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800851
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800852 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
853 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800854 metadata = {
855 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800856 "pre-device": GetOemProperty("ro.product.device", oem_props,
857 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -0800858 OPTIONS.info_dict),
859 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
860 }
861
862 device_specific = common.DeviceSpecificParams(
863 input_zip=input_zip,
864 input_version=OPTIONS.info_dict["recovery_api_version"],
865 output_zip=output_zip,
866 script=script,
867 input_tmp=OPTIONS.input_tmp,
868 metadata=metadata,
869 info_dict=OPTIONS.info_dict)
870
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800871 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800872
873 script.Print("Verifying device images against %s..." % target_fp)
874 script.AppendExtra("")
875
876 script.Print("Verifying boot...")
877 boot_img = common.GetBootableImage(
878 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
879 boot_type, boot_device = common.GetTypeAndDevice(
880 "/boot", OPTIONS.info_dict)
881 script.Verify("%s:%s:%d:%s" % (
882 boot_type, boot_device, boot_img.size, boot_img.sha1))
883 script.AppendExtra("")
884
885 script.Print("Verifying recovery...")
886 recovery_img = common.GetBootableImage(
887 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
888 recovery_type, recovery_device = common.GetTypeAndDevice(
889 "/recovery", OPTIONS.info_dict)
890 script.Verify("%s:%s:%d:%s" % (
891 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
892 script.AppendExtra("")
893
Tao Bao7e0f1602017-03-06 15:50:08 -0800894 system_tgt = GetImage("system", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800895 system_tgt.ResetFileMap()
896 system_diff = common.BlockDifference("system", system_tgt, src=None)
897 system_diff.WriteStrictVerifyScript(script)
898
899 if HasVendorPartition(input_zip):
Tao Bao7e0f1602017-03-06 15:50:08 -0800900 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800901 vendor_tgt.ResetFileMap()
902 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
903 vendor_diff.WriteStrictVerifyScript(script)
904
905 # Device specific partitions, such as radio, bootloader and etc.
906 device_specific.VerifyOTA_Assertions()
907
908 script.SetProgress(1.0)
909 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800910 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -0800911 WriteMetadata(metadata, output_zip)
912
913
Tao Baoc098e9e2016-01-07 13:03:56 -0800914def WriteABOTAPackageWithBrilloScript(target_file, output_file,
915 source_file=None):
916 """Generate an Android OTA package that has A/B update payload."""
917
Tao Bao2dd1c482017-02-03 16:49:39 -0800918 def ComputeStreamingMetadata(zip_file, reserve_space=False,
919 expected_length=None):
920 """Compute the streaming metadata for a given zip.
921
922 When 'reserve_space' is True, we reserve extra space for the offset and
923 length of the metadata entry itself, although we don't know the final
924 values until the package gets signed. This function will be called again
925 after signing. We then write the actual values and pad the string to the
926 length we set earlier. Note that we can't use the actual length of the
927 metadata entry in the second run. Otherwise the offsets for other entries
928 will be changing again.
929 """
Tao Baoc96316c2017-01-24 22:10:49 -0800930
931 def ComputeEntryOffsetSize(name):
932 """Compute the zip entry offset and size."""
933 info = zip_file.getinfo(name)
934 offset = info.header_offset + len(info.FileHeader())
935 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -0800936 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -0800937
938 # payload.bin and payload_properties.txt must exist.
939 offsets = [ComputeEntryOffsetSize('payload.bin'),
940 ComputeEntryOffsetSize('payload_properties.txt')]
941
942 # care_map.txt is available only if dm-verity is enabled.
943 if 'care_map.txt' in zip_file.namelist():
944 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -0800945
946 # 'META-INF/com/android/metadata' is required. We don't know its actual
947 # offset and length (as well as the values for other entries). So we
948 # reserve 10-byte as a placeholder, which is to cover the space for metadata
949 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
950 # beginning of the zip), as well as the possible value changes in other
951 # entries.
952 if reserve_space:
953 offsets.append('metadata:' + ' ' * 10)
954 else:
955 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
956
957 value = ','.join(offsets)
958 if expected_length is not None:
959 assert len(value) <= expected_length, \
960 'Insufficient reserved space: reserved=%d, actual=%d' % (
961 expected_length, len(value))
962 value += ' ' * (expected_length - len(value))
963 return value
Tao Baoc96316c2017-01-24 22:10:49 -0800964
Alex Deymod8d96ec2016-06-10 16:38:31 -0700965 # The place where the output from the subprocess should go.
966 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
967
Tao Baoc098e9e2016-01-07 13:03:56 -0800968 # Setup signing keys.
969 if OPTIONS.package_key is None:
970 OPTIONS.package_key = OPTIONS.info_dict.get(
971 "default_system_dev_certificate",
972 "build/target/product/security/testkey")
973
Tao Baodea0f8b2016-06-20 17:55:06 -0700974 # A/B updater expects a signing key in RSA format. Gets the key ready for
975 # later use in step 3, unless a payload_signer has been specified.
976 if OPTIONS.payload_signer is None:
977 cmd = ["openssl", "pkcs8",
978 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
979 "-inform", "DER", "-nocrypt"]
980 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
981 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -0700982 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
983 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -0700984 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -0800985
Tao Baodea0f8b2016-06-20 17:55:06 -0700986 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -0800987 temp_zip_file = tempfile.NamedTemporaryFile()
988 output_zip = zipfile.ZipFile(temp_zip_file, "w",
989 compression=zipfile.ZIP_DEFLATED)
990
991 # Metadata to comply with Android OTA package format.
992 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800993 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -0800994 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -0800995 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -0800996
997 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800998 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -0800999 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001000 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1001 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001002 "pre-device": GetOemProperty("ro.product.device", oem_props,
1003 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001004 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001005 "ota-required-cache": "0",
1006 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001007 }
1008
1009 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001010 metadata["pre-build"] = CalculateFingerprint(oem_props,
1011 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001012 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001013 metadata["pre-build-incremental"] = GetBuildProp(
1014 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001015
Tao Baob31892e2017-02-07 11:21:17 -08001016 HandleDowngradeMetadata(metadata)
1017 else:
1018 metadata["post-timestamp"] = GetBuildProp(
1019 "ro.build.date.utc", OPTIONS.info_dict)
1020
Tao Baoc098e9e2016-01-07 13:03:56 -08001021 # 1. Generate payload.
1022 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1023 cmd = ["brillo_update_payload", "generate",
1024 "--payload", payload_file,
1025 "--target_image", target_file]
1026 if source_file is not None:
1027 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001028 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1029 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001030 assert p1.returncode == 0, "brillo_update_payload generate failed"
1031
1032 # 2. Generate hashes of the payload and metadata files.
1033 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1034 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1035 cmd = ["brillo_update_payload", "hash",
1036 "--unsigned_payload", payload_file,
1037 "--signature_size", "256",
1038 "--metadata_hash_file", metadata_sig_file,
1039 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001040 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1041 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001042 assert p1.returncode == 0, "brillo_update_payload hash failed"
1043
1044 # 3. Sign the hashes and insert them back into the payload file.
1045 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1046 suffix=".bin")
1047 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1048 suffix=".bin")
1049 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001050 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001051 cmd = [OPTIONS.payload_signer]
1052 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001053 else:
1054 cmd = ["openssl", "pkeyutl", "-sign",
1055 "-inkey", rsa_key,
1056 "-pkeyopt", "digest:sha256"]
1057 cmd.extend(["-in", payload_sig_file,
1058 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001059 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1060 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001061 assert p1.returncode == 0, "openssl sign payload failed"
1062
1063 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001064 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001065 cmd = [OPTIONS.payload_signer]
1066 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001067 else:
1068 cmd = ["openssl", "pkeyutl", "-sign",
1069 "-inkey", rsa_key,
1070 "-pkeyopt", "digest:sha256"]
1071 cmd.extend(["-in", metadata_sig_file,
1072 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001073 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1074 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001075 assert p1.returncode == 0, "openssl sign metadata failed"
1076
1077 # 3c. Insert the signatures back into the payload file.
1078 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1079 suffix=".bin")
1080 cmd = ["brillo_update_payload", "sign",
1081 "--unsigned_payload", payload_file,
1082 "--payload", signed_payload_file,
1083 "--signature_size", "256",
1084 "--metadata_signature_file", signed_metadata_sig_file,
1085 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001086 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1087 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001088 assert p1.returncode == 0, "brillo_update_payload sign failed"
1089
Alex Deymo19241c12016-02-04 22:29:29 -08001090 # 4. Dump the signed payload properties.
1091 properties_file = common.MakeTempFile(prefix="payload-properties-",
1092 suffix=".txt")
1093 cmd = ["brillo_update_payload", "properties",
1094 "--payload", signed_payload_file,
1095 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001096 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1097 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001098 assert p1.returncode == 0, "brillo_update_payload properties failed"
1099
Tao Bao7c5dc572016-06-14 17:48:11 -07001100 if OPTIONS.wipe_user_data:
1101 with open(properties_file, "a") as f:
1102 f.write("POWERWASH=1\n")
1103 metadata["ota-wipe"] = "yes"
1104
Tao Baoc96316c2017-01-24 22:10:49 -08001105 # Add the signed payload file and properties into the zip. In order to
1106 # support streaming, we pack payload.bin, payload_properties.txt and
1107 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1108 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001109 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1110 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001111 common.ZipWrite(output_zip, properties_file,
1112 arcname="payload_properties.txt",
1113 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001114
Tianjie Xucfa86222016-03-07 16:31:19 -08001115 # If dm-verity is supported for the device, copy contents of care_map
1116 # into A/B OTA package.
1117 if OPTIONS.info_dict.get("verity") == "true":
Tao Baob1e59b82017-04-17 18:40:18 +00001118 target_zip = zipfile.ZipFile(target_file, "r")
Tianjie Xucfa86222016-03-07 16:31:19 -08001119 care_map_path = "META/care_map.txt"
1120 namelist = target_zip.namelist()
1121 if care_map_path in namelist:
1122 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001123 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1124 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001125 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001126 print("Warning: cannot find care map file in target_file package")
Tao Baob1e59b82017-04-17 18:40:18 +00001127 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001128
Tao Bao2dd1c482017-02-03 16:49:39 -08001129 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001130 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001131 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001132 WriteMetadata(metadata, output_zip)
1133 common.ZipClose(output_zip)
1134
Tao Bao2dd1c482017-02-03 16:49:39 -08001135 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1136 # zip entries, as well as padding the entry headers. We do a preliminary
1137 # signing (with an incomplete metadata entry) to allow that to happen. Then
1138 # compute the zip entry offsets, write back the final metadata and do the
1139 # final signing.
1140 prelim_signing = tempfile.NamedTemporaryFile()
1141 SignOutput(temp_zip_file.name, prelim_signing.name)
1142 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001143
Tao Bao2dd1c482017-02-03 16:49:39 -08001144 # Open the signed zip. Compute the final metadata that's needed for streaming.
1145 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1146 compression=zipfile.ZIP_DEFLATED)
1147 expected_length = len(metadata['ota-streaming-property-files'])
1148 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1149 prelim_zip, reserve_space=False, expected_length=expected_length)
1150
1151 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1152 final_signing = tempfile.NamedTemporaryFile()
1153 output_zip = zipfile.ZipFile(final_signing, "w",
1154 compression=zipfile.ZIP_DEFLATED)
1155 for item in prelim_zip.infolist():
1156 if item.filename == METADATA_NAME:
1157 continue
1158
1159 data = prelim_zip.read(item.filename)
1160 out_info = copy.copy(item)
1161 common.ZipWriteStr(output_zip, out_info, data)
1162
1163 # Now write the final metadata entry.
1164 WriteMetadata(metadata, output_zip)
1165 common.ZipClose(prelim_zip)
1166 common.ZipClose(output_zip)
1167
1168 # Re-sign the package after updating the metadata entry.
1169 SignOutput(final_signing.name, output_file)
1170 final_signing.close()
1171
1172 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001173 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001174 actual = metadata['ota-streaming-property-files'].strip()
1175 expected = ComputeStreamingMetadata(output_zip)
1176 assert actual == expected, \
1177 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001178 common.ZipClose(output_zip)
1179
Tao Baoc098e9e2016-01-07 13:03:56 -08001180
Doug Zongkereef39442009-04-02 12:14:19 -07001181def main(argv):
1182
1183 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001184 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001185 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001186 elif o in ("-k", "--package_key"):
1187 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001188 elif o in ("-i", "--incremental_from"):
1189 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001190 elif o == "--full_radio":
1191 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001192 elif o == "--full_bootloader":
1193 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001194 elif o in ("-w", "--wipe_user_data"):
1195 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001196 elif o == "--downgrade":
1197 OPTIONS.downgrade = True
1198 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001199 elif o == "--override_timestamp":
1200 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001201 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001202 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001203 elif o == "--oem_no_mount":
1204 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001205 elif o in ("-e", "--extra_script"):
1206 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001207 elif o in ("-t", "--worker_threads"):
1208 if a.isdigit():
1209 OPTIONS.worker_threads = int(a)
1210 else:
1211 raise ValueError("Cannot parse value %r for option %r - only "
1212 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001213 elif o in ("-2", "--two_step"):
1214 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001215 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001216 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001217 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001218 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001219 elif o == "--block":
1220 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001221 elif o in ("-b", "--binary"):
1222 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001223 elif o in ("--no_fallback_to_full",):
1224 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001225 elif o == "--stash_threshold":
1226 try:
1227 OPTIONS.stash_threshold = float(a)
1228 except ValueError:
1229 raise ValueError("Cannot parse value %r for option %r - expecting "
1230 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001231 elif o == "--gen_verify":
1232 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001233 elif o == "--log_diff":
1234 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001235 elif o == "--payload_signer":
1236 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001237 elif o == "--payload_signer_args":
1238 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001239 elif o == "--extracted_input_target_files":
1240 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001241 else:
1242 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001243 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001244
1245 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08001246 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001247 extra_long_opts=[
1248 "board_config=",
1249 "package_key=",
1250 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001251 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001252 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001253 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001254 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001255 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001256 "extra_script=",
1257 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001258 "two_step",
1259 "no_signing",
1260 "block",
1261 "binary=",
1262 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001263 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001264 "verify",
1265 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001266 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001267 "gen_verify",
1268 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001269 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001270 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001271 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001272 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001273
1274 if len(args) != 2:
1275 common.Usage(__doc__)
1276 sys.exit(1)
1277
Tao Bao5d182562016-02-23 11:38:39 -08001278 if OPTIONS.downgrade:
1279 # Sanity check to enforce a data wipe.
1280 if not OPTIONS.wipe_user_data:
1281 raise ValueError("Cannot downgrade without a data wipe")
1282
1283 # We should only allow downgrading incrementals (as opposed to full).
1284 # Otherwise the device may go back from arbitrary build with this full
1285 # OTA package.
1286 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001287 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001288
Tao Bao3e6161a2017-02-28 11:48:48 -08001289 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1290 "Cannot have --downgrade AND --override_timestamp both"
1291
Tao Baoc098e9e2016-01-07 13:03:56 -08001292 # Load the dict file from the zip directly to have a peek at the OTA type.
1293 # For packages using A/B update, unzipping is not needed.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001294 if OPTIONS.extracted_input is not None:
1295 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input)
1296 else:
1297 input_zip = zipfile.ZipFile(args[0], "r")
1298 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1299 common.ZipClose(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001300
1301 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1302
1303 if ab_update:
1304 if OPTIONS.incremental_source is not None:
1305 OPTIONS.target_info_dict = OPTIONS.info_dict
1306 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1307 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1308 common.ZipClose(source_zip)
1309
1310 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001311 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001312 common.DumpInfoDict(OPTIONS.info_dict)
1313
1314 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001315 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001316 common.DumpInfoDict(OPTIONS.source_info_dict)
1317
1318 WriteABOTAPackageWithBrilloScript(
1319 target_file=args[0],
1320 output_file=args[1],
1321 source_file=OPTIONS.incremental_source)
1322
Tao Bao89fbb0f2017-01-10 10:47:58 -08001323 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001324 return
1325
Doug Zongker1c390a22009-05-14 19:06:36 -07001326 if OPTIONS.extra_script is not None:
1327 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1328
Dan Willemsencea5cd22017-03-21 14:44:27 -07001329 if OPTIONS.extracted_input is not None:
1330 OPTIONS.input_tmp = OPTIONS.extracted_input
1331 OPTIONS.target_tmp = OPTIONS.input_tmp
1332 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp)
1333 input_zip = zipfile.ZipFile(args[0], "r")
1334 else:
1335 print("unzipping target target-files...")
1336 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1337 args[0], UNZIP_PATTERN)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001338
Dan Willemsencea5cd22017-03-21 14:44:27 -07001339 OPTIONS.target_tmp = OPTIONS.input_tmp
1340 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001341
Doug Zongker37974732010-09-16 17:44:38 -07001342 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001343 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07001344 common.DumpInfoDict(OPTIONS.info_dict)
1345
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001346 # If the caller explicitly specified the device-specific extensions
1347 # path via -s/--device_specific, use that. Otherwise, use
1348 # META/releasetools.py if it is present in the target target_files.
1349 # Otherwise, take the path of the file from 'tool_extensions' in the
1350 # info dict and look for that in the local filesystem, relative to
1351 # the current directory.
1352
Doug Zongker37974732010-09-16 17:44:38 -07001353 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001354 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1355 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001356 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001357 OPTIONS.device_specific = from_input
1358 else:
1359 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1360
Doug Zongker37974732010-09-16 17:44:38 -07001361 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001362 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001363
Tao Baoc098e9e2016-01-07 13:03:56 -08001364 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001365 raise common.ExternalError(
1366 "--- target build has specified no recovery ---")
1367
Tao Bao767e3ac2015-11-10 12:19:19 -08001368 # Use the default key to sign the package if not specified with package_key.
1369 if not OPTIONS.no_signing:
1370 if OPTIONS.package_key is None:
1371 OPTIONS.package_key = OPTIONS.info_dict.get(
1372 "default_system_dev_certificate",
1373 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001374
Tao Bao767e3ac2015-11-10 12:19:19 -08001375 # Set up the output zip. Create a temporary zip file if signing is needed.
1376 if OPTIONS.no_signing:
1377 if os.path.exists(args[1]):
1378 os.unlink(args[1])
1379 output_zip = zipfile.ZipFile(args[1], "w",
1380 compression=zipfile.ZIP_DEFLATED)
1381 else:
1382 temp_zip_file = tempfile.NamedTemporaryFile()
1383 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1384 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001385
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001386 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001387 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001388 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001389 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001390 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001391
Tao Bao9bc6bb22015-11-09 16:58:28 -08001392 # Generate a verify package.
1393 if OPTIONS.gen_verify:
1394 WriteVerifyPackage(input_zip, output_zip)
1395
Tao Bao767e3ac2015-11-10 12:19:19 -08001396 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001397 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001398 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001399
1400 # Generate an incremental OTA. It will fall back to generate a full OTA on
1401 # failure unless no_fallback_to_full is specified.
1402 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001403 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001404 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001405 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001406 UNZIP_PATTERN)
Tao Bao767e3ac2015-11-10 12:19:19 -08001407 OPTIONS.target_info_dict = OPTIONS.info_dict
1408 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1409 OPTIONS.source_tmp)
1410 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001411 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001412 common.DumpInfoDict(OPTIONS.source_info_dict)
1413 try:
Tao Bao457cbf62017-03-06 09:56:01 -08001414 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001415 if OPTIONS.log_diff:
1416 out_file = open(OPTIONS.log_diff, 'w')
1417 import target_files_diff
1418 target_files_diff.recursiveDiff('',
1419 OPTIONS.source_tmp,
1420 OPTIONS.input_tmp,
1421 out_file)
1422 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001423 except ValueError:
1424 if not OPTIONS.fallback_to_full:
1425 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08001426 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001427 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001428 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001429
Tao Bao767e3ac2015-11-10 12:19:19 -08001430 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001431
Tao Bao767e3ac2015-11-10 12:19:19 -08001432 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001433 if not OPTIONS.no_signing:
1434 SignOutput(temp_zip_file.name, args[1])
1435 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001436
Tao Bao89fbb0f2017-01-10 10:47:58 -08001437 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001438
1439
1440if __name__ == '__main__':
1441 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001442 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001443 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001444 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001445 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001446 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001447 finally:
1448 common.Cleanup()