blob: 60e2e5ce318a9ffb65041c2e9556396c5a26134a [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 Zongkerafb32ea2011-09-22 10:28:04 -070024 -k (--package_key) <key> Key to use to sign the package (default is
25 the value of default_system_dev_certificate from the input
26 target-files's META/misc_info.txt, or
27 "build/target/product/security/testkey" if that value is not
28 specified).
29
30 For incremental OTAs, the default value is based on the source
31 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070032
33 -i (--incremental_from) <file>
34 Generate an incremental OTA using the given target-files zip as
35 the starting build.
36
Tao Bao43078aa2015-04-21 14:32:35 -070037 --full_radio
38 When generating an incremental OTA, always include a full copy of
39 radio image. This option is only meaningful when -i is specified,
40 because a full radio is always included in a full OTA if applicable.
41
leozwangaa6c1a12015-08-14 10:57:58 -070042 --full_bootloader
43 Similar to --full_radio. When generating an incremental OTA, always
44 include a full copy of bootloader image.
45
Tao Baoedb35b82017-10-30 16:07:13 -070046 --verify
47 Remount and verify the checksums of the files written to the system and
48 vendor (if used) partitions. Non-A/B incremental OTAs only.
Michael Runge63f01de2014-10-28 19:24:19 -070049
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080050 -o (--oem_settings) <main_file[,additional_files...]>
51 Comma seperated list of files used to specify the expected OEM-specific
52 properties on the OEM partition of the intended device.
53 Multiple expected values can be used by providing multiple files.
54
Tao Bao8608cde2016-02-25 19:49:55 -080055 --oem_no_mount
56 For devices with OEM-specific properties but without an OEM partition,
57 do not mount the OEM partition in the updater-script. This should be
58 very rarely used, since it's expected to have a dedicated OEM partition
59 for OEM-specific properties. Only meaningful when -o is specified.
60
Tao Bao337633f2017-12-06 15:20:19 -080061 --wipe_user_data
Doug Zongkerdbfaae52009-04-21 17:12:54 -070062 Generate an OTA package that will wipe the user data partition
63 when installed.
64
Tao Bao5d182562016-02-23 11:38:39 -080065 --downgrade
66 Intentionally generate an incremental OTA that updates from a newer
67 build to an older one (based on timestamp comparison). "post-timestamp"
68 will be replaced by "ota-downgrade=yes" in the metadata file. A data
69 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080070 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080071 the OTA package, unless --binary flag is specified. Please also check the
72 doc for --override_timestamp below.
73
74 --override_timestamp
75 Intentionally generate an incremental OTA that updates from a newer
76 build to an older one (based on timestamp comparison), by overriding the
77 timestamp in package metadata. This differs from --downgrade flag: we
78 know for sure this is NOT an actual downgrade case, but two builds are
79 cut in a reverse order. A legit use case is that we cut a new build C
80 (after having A and B), but want to enfore an update path of A -> C -> B.
81 Specifying --downgrade may not help since that would enforce a data wipe
82 for C -> B update. The value of "post-timestamp" will be set to the newer
83 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080084
Doug Zongker1c390a22009-05-14 19:06:36 -070085 -e (--extra_script) <file>
86 Insert the contents of file at the end of the update script.
87
Doug Zongker9b23f2c2013-11-25 14:44:12 -080088 -2 (--two_step)
89 Generate a 'two-step' OTA package, where recovery is updated
90 first, so that any changes made to the system partition are done
91 using the new recovery (new kernel, etc.).
92
Doug Zongker26e66192014-02-20 13:22:07 -080093 --block
Tao Bao457cbf62017-03-06 09:56:01 -080094 Generate a block-based OTA for non-A/B device. We have deprecated the
95 support for file-based OTA since O. Block-based OTA will be used by
96 default for all non-A/B devices. Keeping this flag here to not break
97 existing callers.
Doug Zongker26e66192014-02-20 13:22:07 -080098
Doug Zongker25568482014-03-03 10:21:27 -080099 -b (--binary) <file>
100 Use the given binary as the update-binary in the output package,
101 instead of the binary in the build's target_files. Use for
102 development only.
103
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200104 -t (--worker_threads) <int>
105 Specifies the number of worker-threads that will be used when
106 generating patches for incremental updates (defaults to 3).
107
Tao Bao8dcf7382015-05-21 14:09:49 -0700108 --stash_threshold <float>
109 Specifies the threshold that will be used to compute the maximum
110 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800111
Tao Baod62c6032015-11-30 09:40:20 -0800112 --log_diff <file>
113 Generate a log file that shows the differences in the source and target
114 builds for an incremental package. This option is only meaningful when
115 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700116
117 --payload_signer <signer>
118 Specify the signer when signing the payload and metadata for A/B OTAs.
119 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
120 with the package private key. If the private key cannot be accessed
121 directly, a payload signer that knows how to do that should be specified.
122 The signer will be supplied with "-inkey <path_to_key>",
123 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700124
125 --payload_signer_args <args>
126 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700127"""
128
Tao Bao89fbb0f2017-01-10 10:47:58 -0800129from __future__ import print_function
130
Doug Zongkereef39442009-04-02 12:14:19 -0700131import sys
132
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800133if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800134 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700135 sys.exit(1)
136
Doug Zongkerfc44a512014-08-26 13:10:25 -0700137import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800138import os.path
Tao Baoc098e9e2016-01-07 13:03:56 -0800139import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700140import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700141import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700142import zipfile
143
144import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700145import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700146import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700147
148OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700149OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700150OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700151OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700152OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700153OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800154OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800155OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700156OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700157OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
158if OPTIONS.worker_threads == 0:
159 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800160OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900161OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800162OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800163OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700164OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800165OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700166OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700167OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700168OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700169# Stash size cannot exceed cache_size * threshold.
170OPTIONS.cache_size = None
171OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800172OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700173OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700174OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700175OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200176OPTIONS.key_passwords = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700177
Tao Bao2dd1c482017-02-03 16:49:39 -0800178METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800179UNZIP_PATTERN = ['IMAGES/*', 'META/*']
180
Tao Bao2dd1c482017-02-03 16:49:39 -0800181
Doug Zongkereef39442009-04-02 12:14:19 -0700182def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200183 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700184
Doug Zongker951495f2009-08-14 12:44:19 -0700185 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
186 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700187
188
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800189def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700190 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700191 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700192 device = GetBuildProp("ro.product.device", info_dict)
193 script.AssertDevice(device)
194 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800195 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700196 raise common.ExternalError(
197 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700198 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800199 values = []
200 for oem_dict in oem_dicts:
201 if oem_dict.get(prop):
202 values.append(oem_dict[prop])
203 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700204 raise common.ExternalError(
205 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800206 script.AssertOemProperty(prop, values)
207
208
Tao Baoebce6972017-03-06 10:22:20 -0800209def _LoadOemDicts(script, recovery_mount_options=None):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800210 """Returns the list of loaded OEM properties dict."""
211 oem_dicts = None
212 if OPTIONS.oem_source is None:
213 raise common.ExternalError("OEM source required for this build")
Tao Baoebce6972017-03-06 10:22:20 -0800214 if not OPTIONS.oem_no_mount and script:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800215 script.Mount("/oem", recovery_mount_options)
216 oem_dicts = []
217 for oem_file in OPTIONS.oem_source:
218 oem_dicts.append(common.LoadDictionaryFromLines(
219 open(oem_file).readlines()))
220 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700221
Doug Zongkereef39442009-04-02 12:14:19 -0700222
Tao Baod42e97e2016-11-30 12:11:57 -0800223def _WriteRecoveryImageToBoot(script, output_zip):
224 """Find and write recovery image to /boot in two-step OTA.
225
226 In two-step OTAs, we write recovery image to /boot as the first step so that
227 we can reboot to there and install a new recovery image to /recovery.
228 A special "recovery-two-step.img" will be preferred, which encodes the correct
229 path of "/boot". Otherwise the device may show "device is corrupt" message
230 when booting into /boot.
231
232 Fall back to using the regular recovery.img if the two-step recovery image
233 doesn't exist. Note that rebuilding the special image at this point may be
234 infeasible, because we don't have the desired boot signer and keys when
235 calling ota_from_target_files.py.
236 """
237
238 recovery_two_step_img_name = "recovery-two-step.img"
239 recovery_two_step_img_path = os.path.join(
240 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
241 if os.path.exists(recovery_two_step_img_path):
242 recovery_two_step_img = common.GetBootableImage(
243 recovery_two_step_img_name, recovery_two_step_img_name,
244 OPTIONS.input_tmp, "RECOVERY")
245 common.ZipWriteStr(
246 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800247 print("two-step package: using %s in stage 1/3" % (
248 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800249 script.WriteRawImage("/boot", recovery_two_step_img_name)
250 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800251 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800252 # The "recovery.img" entry has been written into package earlier.
253 script.WriteRawImage("/boot", "recovery.img")
254
255
Doug Zongkerc9253822014-02-04 12:17:58 -0800256def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700257 namelist = [name for name in target_files_zip.namelist()]
258 return ("SYSTEM/recovery-from-boot.p" in namelist or
259 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700260
Tao Bao457cbf62017-03-06 09:56:01 -0800261
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700262def HasVendorPartition(target_files_zip):
263 try:
264 target_files_zip.getinfo("VENDOR/")
265 return True
266 except KeyError:
267 return False
268
Tao Bao457cbf62017-03-06 09:56:01 -0800269
Tao Baobcd1d162017-08-26 13:10:26 -0700270def HasTrebleEnabled(target_files_zip, info_dict):
271 return (HasVendorPartition(target_files_zip) and
272 GetBuildProp("ro.treble.enabled", info_dict) == "true")
273
274
Michael Runge6e836112014-04-15 17:40:21 -0700275def GetOemProperty(name, oem_props, oem_dict, info_dict):
276 if oem_props is not None and name in oem_props:
277 return oem_dict[name]
278 return GetBuildProp(name, info_dict)
279
280
281def CalculateFingerprint(oem_props, oem_dict, info_dict):
282 if oem_props is None:
283 return GetBuildProp("ro.build.fingerprint", info_dict)
284 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700285 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
286 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
287 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
288 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700289
Doug Zongkerfc44a512014-08-26 13:10:25 -0700290
Tao Bao7e0f1602017-03-06 15:50:08 -0800291def GetImage(which, tmpdir):
292 """Returns an image object suitable for passing to BlockImageDiff.
293
294 'which' partition must be "system" or "vendor". A prebuilt image and file
295 map must already exist in tmpdir.
296 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700297
298 assert which in ("system", "vendor")
299
300 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700301 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700302
Tao Bao7e0f1602017-03-06 15:50:08 -0800303 # The image and map files must have been created prior to calling
304 # ota_from_target_files.py (since LMP).
305 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700306
Tao Baoff777812015-05-12 11:42:31 -0700307 # Bug: http://b/20939131
308 # In ext4 filesystems, block 0 might be changed even being mounted
309 # R/O. We add it to clobbered_blocks so that it will be written to the
310 # target unconditionally. Note that they are still part of care_map.
311 clobbered_blocks = "0"
312
313 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700314
315
Tao Baobcd1d162017-08-26 13:10:26 -0700316def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip,
317 target_info_dict,
318 source_info_dict=None):
319 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700320
321 Metadata used for on-device compatibility verification is retrieved from
322 target_zip then added to compatibility.zip which is added to the output_zip
323 archive.
324
Tao Baobcd1d162017-08-26 13:10:26 -0700325 Compatibility archive should only be included for devices that have enabled
326 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700327
328 Args:
329 target_zip: Zip file containing the source files to be included for OTA.
330 output_zip: Zip file that will be sent for OTA.
Tao Baobcd1d162017-08-26 13:10:26 -0700331 target_info_dict: The dict that holds the target build info.
332 source_info_dict: The dict that holds the source build info, if generating
333 an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700334 """
335
Tao Baobcd1d162017-08-26 13:10:26 -0700336 def AddCompatibilityArchive(system_updated, vendor_updated):
337 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700338
Tao Baobcd1d162017-08-26 13:10:26 -0700339 Args:
340 system_updated: If True, the system image will be updated and therefore
341 its metadata should be included.
342 vendor_updated: If True, the vendor image will be updated and therefore
343 its metadata should be included.
344 """
345 # Determine what metadata we need. Files are names relative to META/.
346 compatibility_files = []
347 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
348 system_metadata = ("system_manifest.xml", "system_matrix.xml")
349 if vendor_updated:
350 compatibility_files += vendor_metadata
351 if system_updated:
352 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700353
Tao Baobcd1d162017-08-26 13:10:26 -0700354 # Create new archive.
355 compatibility_archive = tempfile.NamedTemporaryFile()
356 compatibility_archive_zip = zipfile.ZipFile(compatibility_archive, "w",
357 compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700358
Tao Baobcd1d162017-08-26 13:10:26 -0700359 # Add metadata.
360 for file_name in compatibility_files:
361 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700362
Tao Baobcd1d162017-08-26 13:10:26 -0700363 if target_file_name in target_zip.namelist():
364 data = target_zip.read(target_file_name)
365 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700366
Tao Baobcd1d162017-08-26 13:10:26 -0700367 # Ensure files are written before we copy into output_zip.
368 compatibility_archive_zip.close()
369
370 # Only add the archive if we have any compatibility info.
371 if compatibility_archive_zip.namelist():
372 common.ZipWrite(output_zip, compatibility_archive.name,
373 arcname="compatibility.zip",
374 compress_type=zipfile.ZIP_STORED)
375
376 # Will only proceed if the target has enabled the Treble support (as well as
377 # having a /vendor partition).
378 if not HasTrebleEnabled(target_zip, target_info_dict):
379 return
380
381 # We don't support OEM thumbprint in Treble world (which calculates
382 # fingerprints in a different way as shown in CalculateFingerprint()).
383 assert not target_info_dict.get("oem_fingerprint_properties")
384
385 # Full OTA carries the info for system/vendor both.
386 if source_info_dict is None:
387 AddCompatibilityArchive(True, True)
388 return
389
390 assert not source_info_dict.get("oem_fingerprint_properties")
391
392 source_fp = GetBuildProp("ro.build.fingerprint", source_info_dict)
393 target_fp = GetBuildProp("ro.build.fingerprint", target_info_dict)
394 system_updated = source_fp != target_fp
395
396 source_fp_vendor = GetVendorBuildProp("ro.vendor.build.fingerprint",
397 source_info_dict)
398 target_fp_vendor = GetVendorBuildProp("ro.vendor.build.fingerprint",
399 target_info_dict)
400 vendor_updated = source_fp_vendor != target_fp_vendor
401
402 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700403
404
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700405def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700406 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700407 # be installed on top of. For now, we expect the API just won't
408 # change very often. Similarly for fstab, it might have changed
409 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700410 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700411
Tao Bao838c68f2016-03-15 19:16:18 +0000412 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700413 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800414 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700415 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800416 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700417
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800418 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
419 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700420 metadata = {
Tao Bao39f3eaf2017-03-09 15:01:11 -0800421 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800422 "pre-device": GetOemProperty("ro.product.device", oem_props,
423 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700424 OPTIONS.info_dict),
425 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
426 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700427
Doug Zongker05d3dea2009-06-22 11:32:31 -0700428 device_specific = common.DeviceSpecificParams(
429 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700430 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700431 output_zip=output_zip,
432 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700433 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700434 metadata=metadata,
435 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700436
Tao Bao457cbf62017-03-06 09:56:01 -0800437 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800438
Tao Bao457cbf62017-03-06 09:56:01 -0800439 metadata["ota-type"] = "BLOCK"
Tao Baod8d14be2016-02-04 14:26:02 -0800440
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700441 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
442 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
443 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700444
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800445 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700446 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800447
448 # Two-step package strategy (in chronological order, which is *not*
449 # the order in which the generated script has things):
450 #
451 # if stage is not "2/3" or "3/3":
452 # write recovery image to boot partition
453 # set stage to "2/3"
454 # reboot to boot partition and restart recovery
455 # else if stage is "2/3":
456 # write recovery image to recovery partition
457 # set stage to "3/3"
458 # reboot to recovery partition and restart recovery
459 # else:
460 # (stage must be "3/3")
461 # set stage to ""
462 # do normal full package installation:
463 # wipe and install system, boot image, etc.
464 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700465 # complete script normally
466 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800467
468 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
469 OPTIONS.input_tmp, "RECOVERY")
470 if OPTIONS.two_step:
471 if not OPTIONS.info_dict.get("multistage_support", None):
472 assert False, "two-step packages not supported by this build"
473 fs = OPTIONS.info_dict["fstab"]["/misc"]
474 assert fs.fs_type.upper() == "EMMC", \
475 "two-step packages only supported on devices with EMMC /misc partitions"
476 bcb_dev = {"bcb_dev": fs.device}
477 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
478 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700479if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800480""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800481
482 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
483 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800484 script.WriteRawImage("/recovery", "recovery.img")
485 script.AppendExtra("""
486set_stage("%(bcb_dev)s", "3/3");
487reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700488else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800489""" % bcb_dev)
490
Tao Baod42e97e2016-11-30 12:11:57 -0800491 # Stage 3/3: Make changes.
492 script.Comment("Stage 3/3")
493
Tao Bao6c55a8a2015-04-08 15:30:27 -0700494 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700495 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700496
Doug Zongkere5ff5902012-01-17 10:55:37 -0800497 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700498
Doug Zongker01ce19c2014-02-04 13:48:15 -0800499 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700500
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700501 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800502 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700503 if HasVendorPartition(input_zip):
504 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700505
Michael Runge7cd99ba2014-10-22 17:21:48 -0700506 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
507
Doug Zongker4b9596f2014-06-09 14:15:45 -0700508 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800509
Tao Bao457cbf62017-03-06 09:56:01 -0800510 # Full OTA is done as an "incremental" against an empty source image. This
511 # has the effect of writing new data from the package to the entire
512 # partition, but lets us reuse the updater code that writes incrementals to
513 # do it.
514 system_tgt = GetImage("system", OPTIONS.input_tmp)
515 system_tgt.ResetFileMap()
516 system_diff = common.BlockDifference("system", system_tgt, src=None)
517 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700518
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700519 boot_img = common.GetBootableImage(
520 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800521
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700522 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700523 script.ShowProgress(0.1, 0)
524
Tao Bao457cbf62017-03-06 09:56:01 -0800525 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
526 vendor_tgt.ResetFileMap()
527 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
528 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700529
Tao Baobcd1d162017-08-26 13:10:26 -0700530 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip,
531 OPTIONS.info_dict)
532
Doug Zongker37974732010-09-16 17:44:38 -0700533 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700534 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700535
Doug Zongker01ce19c2014-02-04 13:48:15 -0800536 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700537 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700538
Doug Zongker01ce19c2014-02-04 13:48:15 -0800539 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700540 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700541
Doug Zongker1c390a22009-05-14 19:06:36 -0700542 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700543 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700544
Doug Zongker14833602010-02-02 13:12:04 -0800545 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800546
Doug Zongker922206e2014-03-04 13:16:24 -0800547 if OPTIONS.wipe_user_data:
548 script.ShowProgress(0.1, 10)
549 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700550
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800551 if OPTIONS.two_step:
552 script.AppendExtra("""
553set_stage("%(bcb_dev)s", "");
554""" % bcb_dev)
555 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800556
557 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
558 script.Comment("Stage 1/3")
559 _WriteRecoveryImageToBoot(script, output_zip)
560
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800561 script.AppendExtra("""
562set_stage("%(bcb_dev)s", "2/3");
563reboot_now("%(bcb_dev)s", "");
564endif;
565endif;
566""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800567
Tao Bao5d182562016-02-23 11:38:39 -0800568 script.SetProgress(1)
569 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800570 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700571 WriteMetadata(metadata, output_zip)
572
Doug Zongkerfc44a512014-08-26 13:10:25 -0700573
Doug Zongker2ea21062010-04-28 16:05:21 -0700574def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800575 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
576 common.ZipWriteStr(output_zip, METADATA_NAME, value,
577 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700578
Doug Zongkerfc44a512014-08-26 13:10:25 -0700579
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700580def GetBuildProp(prop, info_dict):
Tao Baobcd1d162017-08-26 13:10:26 -0700581 """Returns the inquired build property from a given info_dict."""
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700582 try:
583 return info_dict.get("build.prop", {})[prop]
584 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700585 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700586
Doug Zongkerfc44a512014-08-26 13:10:25 -0700587
Tao Baobcd1d162017-08-26 13:10:26 -0700588def GetVendorBuildProp(prop, info_dict):
589 """Returns the inquired vendor build property from a given info_dict."""
590 try:
591 return info_dict.get("vendor.build.prop", {})[prop]
592 except KeyError:
593 raise common.ExternalError(
594 "couldn't find %s in vendor.build.prop" % (prop,))
595
596
Tao Baob31892e2017-02-07 11:21:17 -0800597def HandleDowngradeMetadata(metadata):
598 # Only incremental OTAs are allowed to reach here.
599 assert OPTIONS.incremental_source is not None
600
601 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
602 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
603 is_downgrade = long(post_timestamp) < long(pre_timestamp)
604
605 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800606 if not is_downgrade:
607 raise RuntimeError("--downgrade specified but no downgrade detected: "
608 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800609 metadata["ota-downgrade"] = "yes"
610 elif OPTIONS.timestamp:
611 if not is_downgrade:
612 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
613 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
614 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800615 else:
616 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800617 raise RuntimeError("Downgrade detected based on timestamp check: "
618 "pre: %s, post: %s. Need to specify --timestamp OR "
619 "--downgrade to allow building the incremental." % (
620 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800621 metadata["post-timestamp"] = post_timestamp
622
623
Geremy Condra36bd3652014-02-06 19:45:10 -0800624def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
625 source_version = OPTIONS.source_info_dict["recovery_api_version"]
626 target_version = OPTIONS.target_info_dict["recovery_api_version"]
627
628 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700629 print("WARNING: generating edify script for a source that "
630 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700631 script = edify_generator.EdifyGenerator(
632 source_version, OPTIONS.target_info_dict,
633 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800634
Tao Bao3806c232015-07-05 21:08:33 -0700635 recovery_mount_options = OPTIONS.source_info_dict.get(
636 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700637 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
638 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800639 oem_dicts = None
640 if source_oem_props and target_oem_props:
641 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700642
Dan Albert8b72aef2015-03-23 19:13:21 -0700643 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700644 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800645 oem_dicts and oem_dicts[0],
646 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800647 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700648 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800649
Tao Baob31892e2017-02-07 11:21:17 -0800650 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800651
Geremy Condra36bd3652014-02-06 19:45:10 -0800652 device_specific = common.DeviceSpecificParams(
653 source_zip=source_zip,
654 source_version=source_version,
655 target_zip=target_zip,
656 target_version=target_version,
657 output_zip=output_zip,
658 script=script,
659 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700660 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800661
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800662 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700663 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800664 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700665 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800666 metadata["pre-build"] = source_fp
667 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700668 metadata["pre-build-incremental"] = GetBuildProp(
669 "ro.build.version.incremental", OPTIONS.source_info_dict)
670 metadata["post-build-incremental"] = GetBuildProp(
671 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800672
673 source_boot = common.GetBootableImage(
674 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
675 OPTIONS.source_info_dict)
676 target_boot = common.GetBootableImage(
677 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
678 updating_boot = (not OPTIONS.two_step and
679 (source_boot.data != target_boot.data))
680
Geremy Condra36bd3652014-02-06 19:45:10 -0800681 target_recovery = common.GetBootableImage(
682 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800683
Tao Bao7e0f1602017-03-06 15:50:08 -0800684 system_src = GetImage("system", OPTIONS.source_tmp)
685 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700686
687 blockimgdiff_version = 1
688 if OPTIONS.info_dict:
689 blockimgdiff_version = max(
690 int(i) for i in
691 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
692
Tao Baof8acad12016-07-07 09:09:58 -0700693 # Check the first block of the source system partition for remount R/W only
694 # if the filesystem is ext4.
695 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
696 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700697 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
698 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
699 # b) the blocks listed in block map may not contain all the bytes for a given
700 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700701 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
702 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
703 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700704 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800705 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700706 version=blockimgdiff_version,
707 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700708
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700709 if HasVendorPartition(target_zip):
710 if not HasVendorPartition(source_zip):
711 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800712 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
713 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800714
715 # Check first block of vendor partition for remount R/W only if
716 # disk type is ext4
717 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800718 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700719 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700720 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800721 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700722 version=blockimgdiff_version,
723 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700724 else:
725 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800726
Tao Baobcd1d162017-08-26 13:10:26 -0700727 AddCompatibilityArchiveIfTrebleEnabled(
728 target_zip, output_zip, OPTIONS.target_info_dict,
729 OPTIONS.source_info_dict)
730
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800731 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800732 device_specific.IncrementalOTA_Assertions()
733
734 # Two-step incremental package strategy (in chronological order,
735 # which is *not* the order in which the generated script has
736 # things):
737 #
738 # if stage is not "2/3" or "3/3":
739 # do verification on current system
740 # write recovery image to boot partition
741 # set stage to "2/3"
742 # reboot to boot partition and restart recovery
743 # else if stage is "2/3":
744 # write recovery image to recovery partition
745 # set stage to "3/3"
746 # reboot to recovery partition and restart recovery
747 # else:
748 # (stage must be "3/3")
749 # perform update:
750 # patch system files, etc.
751 # force full install of new boot image
752 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700753 # complete script normally
754 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800755
756 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700757 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800758 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700759 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800760 assert fs.fs_type.upper() == "EMMC", \
761 "two-step packages only supported on devices with EMMC /misc partitions"
762 bcb_dev = {"bcb_dev": fs.device}
763 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
764 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700765if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800766""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800767
768 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
769 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700770 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800771 script.WriteRawImage("/recovery", "recovery.img")
772 script.AppendExtra("""
773set_stage("%(bcb_dev)s", "3/3");
774reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700775else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800776""" % bcb_dev)
777
Tao Baod42e97e2016-11-30 12:11:57 -0800778 # Stage 1/3: (a) Verify the current system.
779 script.Comment("Stage 1/3")
780
Tao Bao6c55a8a2015-04-08 15:30:27 -0700781 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -0800782 script.Print("Source: %s" % (source_fp,))
783 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700784
Geremy Condra36bd3652014-02-06 19:45:10 -0800785 script.Print("Verifying current system...")
786
787 device_specific.IncrementalOTA_VerifyBegin()
788
Tao Bao3e30d972016-03-15 13:20:19 -0700789 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
790 # patching on a device that's already on the target build will damage the
791 # system. Because operations like move don't check the block state, they
792 # always apply the changes unconditionally.
793 if blockimgdiff_version <= 2:
794 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700795 script.AssertSomeFingerprint(source_fp)
796 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700797 script.AssertSomeThumbprint(
798 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700799
800 else: # blockimgdiff_version > 2
801 if source_oem_props is None and target_oem_props is None:
802 script.AssertSomeFingerprint(source_fp, target_fp)
803 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700804 script.AssertSomeThumbprint(
805 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
806 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700807 elif source_oem_props is None and target_oem_props is not None:
808 script.AssertFingerprintOrThumbprint(
809 source_fp,
810 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
811 else:
812 script.AssertFingerprintOrThumbprint(
813 target_fp,
814 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800815
Tao Baod8d14be2016-02-04 14:26:02 -0800816 # Check the required cache size (i.e. stashed blocks).
817 size = []
818 if system_diff:
819 size.append(system_diff.required_cache)
820 if vendor_diff:
821 size.append(vendor_diff.required_cache)
822
Geremy Condra36bd3652014-02-06 19:45:10 -0800823 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700824 boot_type, boot_device = common.GetTypeAndDevice(
825 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800826 d = common.Difference(target_boot, source_boot)
827 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700828 if d is None:
829 include_full_boot = True
830 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
831 else:
832 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800833
Tao Bao89fbb0f2017-01-10 10:47:58 -0800834 print("boot target: %d source: %d diff: %d" % (
835 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800836
Doug Zongkerf8340082014-08-05 10:39:37 -0700837 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800838
Doug Zongkerf8340082014-08-05 10:39:37 -0700839 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
840 (boot_type, boot_device,
841 source_boot.size, source_boot.sha1,
842 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800843 size.append(target_boot.size)
844
845 if size:
846 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800847
848 device_specific.IncrementalOTA_VerifyEnd()
849
850 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800851 # Stage 1/3: (b) Write recovery image to /boot.
852 _WriteRecoveryImageToBoot(script, output_zip)
853
Geremy Condra36bd3652014-02-06 19:45:10 -0800854 script.AppendExtra("""
855set_stage("%(bcb_dev)s", "2/3");
856reboot_now("%(bcb_dev)s", "");
857else
858""" % bcb_dev)
859
Tao Baod42e97e2016-11-30 12:11:57 -0800860 # Stage 3/3: Make changes.
861 script.Comment("Stage 3/3")
862
Jesse Zhao75bcea02015-01-06 10:59:53 -0800863 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700864 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800865 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700866 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800867
Geremy Condra36bd3652014-02-06 19:45:10 -0800868 script.Comment("---- start making changes here ----")
869
870 device_specific.IncrementalOTA_InstallBegin()
871
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700872 system_diff.WriteScript(script, output_zip,
873 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700874
Doug Zongkerfc44a512014-08-26 13:10:25 -0700875 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700876 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800877
878 if OPTIONS.two_step:
879 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
880 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800881 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800882
883 if not OPTIONS.two_step:
884 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700885 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800886 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700887 script.Print("Installing boot image...")
888 script.WriteRawImage("/boot", "boot.img")
889 else:
890 # Produce the boot image by applying a patch to the current
891 # contents of the boot partition, and write it back to the
892 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -0800893 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700894 script.Print("Patching boot image...")
895 script.ShowProgress(0.1, 10)
896 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
897 % (boot_type, boot_device,
898 source_boot.size, source_boot.sha1,
899 target_boot.size, target_boot.sha1),
900 "-",
901 target_boot.size, target_boot.sha1,
902 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800903 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800904 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800905
906 # Do device-specific installation (eg, write radio image).
907 device_specific.IncrementalOTA_InstallEnd()
908
909 if OPTIONS.extra_script is not None:
910 script.AppendExtra(OPTIONS.extra_script)
911
Doug Zongker922206e2014-03-04 13:16:24 -0800912 if OPTIONS.wipe_user_data:
913 script.Print("Erasing user data...")
914 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -0800915 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -0800916
Geremy Condra36bd3652014-02-06 19:45:10 -0800917 if OPTIONS.two_step:
918 script.AppendExtra("""
919set_stage("%(bcb_dev)s", "");
920endif;
921endif;
922""" % bcb_dev)
923
924 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -0800925 # For downgrade OTAs, we prefer to use the update-binary in the source
926 # build that is actually newer than the one in the target build.
927 if OPTIONS.downgrade:
928 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
929 else:
930 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800931 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -0800932 WriteMetadata(metadata, output_zip)
933
Doug Zongker32b527d2014-03-04 10:03:02 -0800934
Tao Baoc098e9e2016-01-07 13:03:56 -0800935def WriteABOTAPackageWithBrilloScript(target_file, output_file,
936 source_file=None):
937 """Generate an Android OTA package that has A/B update payload."""
938
Tao Bao2dd1c482017-02-03 16:49:39 -0800939 def ComputeStreamingMetadata(zip_file, reserve_space=False,
940 expected_length=None):
941 """Compute the streaming metadata for a given zip.
942
943 When 'reserve_space' is True, we reserve extra space for the offset and
944 length of the metadata entry itself, although we don't know the final
945 values until the package gets signed. This function will be called again
946 after signing. We then write the actual values and pad the string to the
947 length we set earlier. Note that we can't use the actual length of the
948 metadata entry in the second run. Otherwise the offsets for other entries
949 will be changing again.
950 """
Tao Baoc96316c2017-01-24 22:10:49 -0800951
952 def ComputeEntryOffsetSize(name):
953 """Compute the zip entry offset and size."""
954 info = zip_file.getinfo(name)
955 offset = info.header_offset + len(info.FileHeader())
956 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -0800957 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -0800958
959 # payload.bin and payload_properties.txt must exist.
960 offsets = [ComputeEntryOffsetSize('payload.bin'),
961 ComputeEntryOffsetSize('payload_properties.txt')]
962
963 # care_map.txt is available only if dm-verity is enabled.
964 if 'care_map.txt' in zip_file.namelist():
965 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -0800966
Tao Bao21803d32017-04-19 10:16:09 -0700967 if 'compatibility.zip' in zip_file.namelist():
968 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
969
Tao Bao2dd1c482017-02-03 16:49:39 -0800970 # 'META-INF/com/android/metadata' is required. We don't know its actual
971 # offset and length (as well as the values for other entries). So we
972 # reserve 10-byte as a placeholder, which is to cover the space for metadata
973 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
974 # beginning of the zip), as well as the possible value changes in other
975 # entries.
976 if reserve_space:
977 offsets.append('metadata:' + ' ' * 10)
978 else:
979 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
980
981 value = ','.join(offsets)
982 if expected_length is not None:
983 assert len(value) <= expected_length, \
984 'Insufficient reserved space: reserved=%d, actual=%d' % (
985 expected_length, len(value))
986 value += ' ' * (expected_length - len(value))
987 return value
Tao Baoc96316c2017-01-24 22:10:49 -0800988
Alex Deymod8d96ec2016-06-10 16:38:31 -0700989 # The place where the output from the subprocess should go.
990 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
991
Tao Baodea0f8b2016-06-20 17:55:06 -0700992 # A/B updater expects a signing key in RSA format. Gets the key ready for
993 # later use in step 3, unless a payload_signer has been specified.
994 if OPTIONS.payload_signer is None:
995 cmd = ["openssl", "pkcs8",
996 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
Christian Oderf63e2cd2017-05-01 22:30:15 +0200997 "-inform", "DER"]
998 pw = OPTIONS.key_passwords[OPTIONS.package_key]
999 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
Tao Baodea0f8b2016-06-20 17:55:06 -07001000 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1001 cmd.extend(["-out", rsa_key])
Christian Oderf63e2cd2017-05-01 22:30:15 +02001002 p1 = common.Run(cmd, verbose=False, stdout=log_file, stderr=subprocess.STDOUT)
Tao Bao6047c242016-06-21 13:35:26 -07001003 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001004 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001005
Tao Baodea0f8b2016-06-20 17:55:06 -07001006 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001007 temp_zip_file = tempfile.NamedTemporaryFile()
1008 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1009 compression=zipfile.ZIP_DEFLATED)
1010
1011 # Metadata to comply with Android OTA package format.
1012 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001013 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001014 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -08001015 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001016
1017 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001018 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001019 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001020 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1021 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001022 "pre-device": GetOemProperty("ro.product.device", oem_props,
1023 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001024 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001025 "ota-required-cache": "0",
1026 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001027 }
1028
1029 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001030 metadata["pre-build"] = CalculateFingerprint(oem_props,
1031 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001032 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001033 metadata["pre-build-incremental"] = GetBuildProp(
1034 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001035
Tao Baob31892e2017-02-07 11:21:17 -08001036 HandleDowngradeMetadata(metadata)
1037 else:
1038 metadata["post-timestamp"] = GetBuildProp(
1039 "ro.build.date.utc", OPTIONS.info_dict)
1040
Tao Baoc098e9e2016-01-07 13:03:56 -08001041 # 1. Generate payload.
1042 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1043 cmd = ["brillo_update_payload", "generate",
1044 "--payload", payload_file,
1045 "--target_image", target_file]
1046 if source_file is not None:
1047 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001048 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1049 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001050 assert p1.returncode == 0, "brillo_update_payload generate failed"
1051
1052 # 2. Generate hashes of the payload and metadata files.
1053 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1054 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1055 cmd = ["brillo_update_payload", "hash",
1056 "--unsigned_payload", payload_file,
1057 "--signature_size", "256",
1058 "--metadata_hash_file", metadata_sig_file,
1059 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001060 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1061 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001062 assert p1.returncode == 0, "brillo_update_payload hash failed"
1063
1064 # 3. Sign the hashes and insert them back into the payload file.
1065 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1066 suffix=".bin")
1067 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1068 suffix=".bin")
1069 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001070 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001071 cmd = [OPTIONS.payload_signer]
1072 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001073 else:
1074 cmd = ["openssl", "pkeyutl", "-sign",
1075 "-inkey", rsa_key,
1076 "-pkeyopt", "digest:sha256"]
1077 cmd.extend(["-in", payload_sig_file,
1078 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001079 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1080 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001081 assert p1.returncode == 0, "openssl sign payload failed"
1082
1083 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001084 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001085 cmd = [OPTIONS.payload_signer]
1086 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001087 else:
1088 cmd = ["openssl", "pkeyutl", "-sign",
1089 "-inkey", rsa_key,
1090 "-pkeyopt", "digest:sha256"]
1091 cmd.extend(["-in", metadata_sig_file,
1092 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001093 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1094 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001095 assert p1.returncode == 0, "openssl sign metadata failed"
1096
1097 # 3c. Insert the signatures back into the payload file.
1098 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1099 suffix=".bin")
1100 cmd = ["brillo_update_payload", "sign",
1101 "--unsigned_payload", payload_file,
1102 "--payload", signed_payload_file,
1103 "--signature_size", "256",
1104 "--metadata_signature_file", signed_metadata_sig_file,
1105 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001106 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1107 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001108 assert p1.returncode == 0, "brillo_update_payload sign failed"
1109
Alex Deymo19241c12016-02-04 22:29:29 -08001110 # 4. Dump the signed payload properties.
1111 properties_file = common.MakeTempFile(prefix="payload-properties-",
1112 suffix=".txt")
1113 cmd = ["brillo_update_payload", "properties",
1114 "--payload", signed_payload_file,
1115 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001116 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1117 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001118 assert p1.returncode == 0, "brillo_update_payload properties failed"
1119
Tao Bao7c5dc572016-06-14 17:48:11 -07001120 if OPTIONS.wipe_user_data:
1121 with open(properties_file, "a") as f:
1122 f.write("POWERWASH=1\n")
1123 metadata["ota-wipe"] = "yes"
1124
Tao Baoc96316c2017-01-24 22:10:49 -08001125 # Add the signed payload file and properties into the zip. In order to
1126 # support streaming, we pack payload.bin, payload_properties.txt and
1127 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1128 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001129 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1130 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001131 common.ZipWrite(output_zip, properties_file,
1132 arcname="payload_properties.txt",
1133 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001134
Tianjie Xucfa86222016-03-07 16:31:19 -08001135 # If dm-verity is supported for the device, copy contents of care_map
1136 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001137 target_zip = zipfile.ZipFile(target_file, "r")
Tianjie Xu6b2e1552017-06-01 11:32:32 -07001138 if (OPTIONS.info_dict.get("verity") == "true" or
Bowgo Tsai3e599ea2017-05-26 18:30:04 +08001139 OPTIONS.info_dict.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001140 care_map_path = "META/care_map.txt"
1141 namelist = target_zip.namelist()
1142 if care_map_path in namelist:
1143 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001144 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1145 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001146 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001147 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001148
Tao Baobcd1d162017-08-26 13:10:26 -07001149 # OPTIONS.source_info_dict must be None for incrementals.
1150 if source_file is None:
1151 assert OPTIONS.source_info_dict is None
Tao Bao21803d32017-04-19 10:16:09 -07001152
Tao Baobcd1d162017-08-26 13:10:26 -07001153 AddCompatibilityArchiveIfTrebleEnabled(
1154 target_zip, output_zip, OPTIONS.info_dict, OPTIONS.source_info_dict)
Tao Bao21803d32017-04-19 10:16:09 -07001155
Tao Bao21803d32017-04-19 10:16:09 -07001156 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001157
Tao Bao2dd1c482017-02-03 16:49:39 -08001158 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001159 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001160 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001161 WriteMetadata(metadata, output_zip)
1162 common.ZipClose(output_zip)
1163
Tao Bao2dd1c482017-02-03 16:49:39 -08001164 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
Tao Bao89d7ab22017-12-14 17:05:33 -08001165 # ZIP entries, as well as padding the entry headers. We do a preliminary
Tao Bao2dd1c482017-02-03 16:49:39 -08001166 # signing (with an incomplete metadata entry) to allow that to happen. Then
Tao Bao89d7ab22017-12-14 17:05:33 -08001167 # compute the ZIP entry offsets, write back the final metadata and do the
Tao Bao2dd1c482017-02-03 16:49:39 -08001168 # final signing.
Tao Bao89d7ab22017-12-14 17:05:33 -08001169 prelim_signing = common.MakeTempFile(suffix='.zip')
1170 SignOutput(temp_zip_file.name, prelim_signing)
Tao Bao2dd1c482017-02-03 16:49:39 -08001171 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001172
Tao Bao2dd1c482017-02-03 16:49:39 -08001173 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Bao89d7ab22017-12-14 17:05:33 -08001174 prelim_signing_zip = zipfile.ZipFile(prelim_signing, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001175 expected_length = len(metadata['ota-streaming-property-files'])
1176 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao89d7ab22017-12-14 17:05:33 -08001177 prelim_signing_zip, reserve_space=False, expected_length=expected_length)
1178 common.ZipClose(prelim_signing_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001179
Tao Bao89d7ab22017-12-14 17:05:33 -08001180 # Replace the METADATA entry.
1181 common.ZipDelete(prelim_signing, METADATA_NAME)
1182 output_zip = zipfile.ZipFile(prelim_signing, 'a',
Tao Bao2dd1c482017-02-03 16:49:39 -08001183 compression=zipfile.ZIP_DEFLATED)
Tao Bao2dd1c482017-02-03 16:49:39 -08001184 WriteMetadata(metadata, output_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001185 common.ZipClose(output_zip)
1186
1187 # Re-sign the package after updating the metadata entry.
Tao Bao89d7ab22017-12-14 17:05:33 -08001188 SignOutput(prelim_signing, output_file)
Tao Bao2dd1c482017-02-03 16:49:39 -08001189
1190 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao89d7ab22017-12-14 17:05:33 -08001191 output_zip = zipfile.ZipFile(output_file, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001192 actual = metadata['ota-streaming-property-files'].strip()
1193 expected = ComputeStreamingMetadata(output_zip)
1194 assert actual == expected, \
1195 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001196 common.ZipClose(output_zip)
1197
Tao Baoc098e9e2016-01-07 13:03:56 -08001198
Doug Zongkereef39442009-04-02 12:14:19 -07001199def main(argv):
1200
1201 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001202 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001203 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001204 elif o in ("-i", "--incremental_from"):
1205 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001206 elif o == "--full_radio":
1207 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001208 elif o == "--full_bootloader":
1209 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001210 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001211 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001212 elif o == "--downgrade":
1213 OPTIONS.downgrade = True
1214 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001215 elif o == "--override_timestamp":
1216 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001217 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001218 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001219 elif o == "--oem_no_mount":
1220 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001221 elif o in ("-e", "--extra_script"):
1222 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001223 elif o in ("-t", "--worker_threads"):
1224 if a.isdigit():
1225 OPTIONS.worker_threads = int(a)
1226 else:
1227 raise ValueError("Cannot parse value %r for option %r - only "
1228 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001229 elif o in ("-2", "--two_step"):
1230 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001231 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001232 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001233 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001234 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001235 elif o == "--block":
1236 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001237 elif o in ("-b", "--binary"):
1238 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001239 elif o in ("--no_fallback_to_full",):
1240 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001241 elif o == "--stash_threshold":
1242 try:
1243 OPTIONS.stash_threshold = float(a)
1244 except ValueError:
1245 raise ValueError("Cannot parse value %r for option %r - expecting "
1246 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001247 elif o == "--log_diff":
1248 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001249 elif o == "--payload_signer":
1250 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001251 elif o == "--payload_signer_args":
1252 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001253 elif o == "--extracted_input_target_files":
1254 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001255 else:
1256 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001257 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001258
1259 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001260 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001261 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001262 "package_key=",
1263 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001264 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001265 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001266 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001267 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001268 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001269 "extra_script=",
1270 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001271 "two_step",
1272 "no_signing",
1273 "block",
1274 "binary=",
1275 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001276 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001277 "verify",
1278 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001279 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001280 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001281 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001282 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001283 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001284 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001285
1286 if len(args) != 2:
1287 common.Usage(__doc__)
1288 sys.exit(1)
1289
Tao Bao5d182562016-02-23 11:38:39 -08001290 if OPTIONS.downgrade:
1291 # Sanity check to enforce a data wipe.
1292 if not OPTIONS.wipe_user_data:
1293 raise ValueError("Cannot downgrade without a data wipe")
1294
1295 # We should only allow downgrading incrementals (as opposed to full).
1296 # Otherwise the device may go back from arbitrary build with this full
1297 # OTA package.
1298 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001299 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001300
Tao Bao3e6161a2017-02-28 11:48:48 -08001301 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1302 "Cannot have --downgrade AND --override_timestamp both"
1303
Tao Baoc098e9e2016-01-07 13:03:56 -08001304 # Load the dict file from the zip directly to have a peek at the OTA type.
1305 # For packages using A/B update, unzipping is not needed.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001306 if OPTIONS.extracted_input is not None:
1307 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input)
1308 else:
1309 input_zip = zipfile.ZipFile(args[0], "r")
1310 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1311 common.ZipClose(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001312
1313 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1314
Christian Oderf63e2cd2017-05-01 22:30:15 +02001315 # Use the default key to sign the package if not specified with package_key.
1316 # package_keys are needed on ab_updates, so always define them if an
1317 # ab_update is getting created.
1318 if not OPTIONS.no_signing or ab_update:
1319 if OPTIONS.package_key is None:
1320 OPTIONS.package_key = OPTIONS.info_dict.get(
1321 "default_system_dev_certificate",
1322 "build/target/product/security/testkey")
1323 # Get signing keys
1324 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1325
Tao Baoc098e9e2016-01-07 13:03:56 -08001326 if ab_update:
1327 if OPTIONS.incremental_source is not None:
1328 OPTIONS.target_info_dict = OPTIONS.info_dict
1329 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1330 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1331 common.ZipClose(source_zip)
1332
1333 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001334 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001335 common.DumpInfoDict(OPTIONS.info_dict)
1336
1337 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001338 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08001339 common.DumpInfoDict(OPTIONS.source_info_dict)
1340
1341 WriteABOTAPackageWithBrilloScript(
1342 target_file=args[0],
1343 output_file=args[1],
1344 source_file=OPTIONS.incremental_source)
1345
Tao Bao89fbb0f2017-01-10 10:47:58 -08001346 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001347 return
1348
Doug Zongker1c390a22009-05-14 19:06:36 -07001349 if OPTIONS.extra_script is not None:
1350 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1351
Dan Willemsencea5cd22017-03-21 14:44:27 -07001352 if OPTIONS.extracted_input is not None:
1353 OPTIONS.input_tmp = OPTIONS.extracted_input
1354 OPTIONS.target_tmp = OPTIONS.input_tmp
1355 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp)
1356 input_zip = zipfile.ZipFile(args[0], "r")
1357 else:
1358 print("unzipping target target-files...")
1359 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1360 args[0], UNZIP_PATTERN)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001361
Dan Willemsencea5cd22017-03-21 14:44:27 -07001362 OPTIONS.target_tmp = OPTIONS.input_tmp
1363 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001364
Doug Zongker37974732010-09-16 17:44:38 -07001365 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001366 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07001367 common.DumpInfoDict(OPTIONS.info_dict)
1368
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001369 # If the caller explicitly specified the device-specific extensions
1370 # path via -s/--device_specific, use that. Otherwise, use
1371 # META/releasetools.py if it is present in the target target_files.
1372 # Otherwise, take the path of the file from 'tool_extensions' in the
1373 # info dict and look for that in the local filesystem, relative to
1374 # the current directory.
1375
Doug Zongker37974732010-09-16 17:44:38 -07001376 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001377 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1378 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001379 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001380 OPTIONS.device_specific = from_input
1381 else:
1382 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1383
Doug Zongker37974732010-09-16 17:44:38 -07001384 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001385 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001386
Tao Baoc098e9e2016-01-07 13:03:56 -08001387 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001388 raise common.ExternalError(
1389 "--- target build has specified no recovery ---")
1390
Tao Bao767e3ac2015-11-10 12:19:19 -08001391 # Set up the output zip. Create a temporary zip file if signing is needed.
1392 if OPTIONS.no_signing:
1393 if os.path.exists(args[1]):
1394 os.unlink(args[1])
1395 output_zip = zipfile.ZipFile(args[1], "w",
1396 compression=zipfile.ZIP_DEFLATED)
1397 else:
1398 temp_zip_file = tempfile.NamedTemporaryFile()
1399 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1400 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001401
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001402 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001403 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001404 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001405 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001406 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001407
Tao Bao767e3ac2015-11-10 12:19:19 -08001408 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001409 if OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001410 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001411
1412 # Generate an incremental OTA. It will fall back to generate a full OTA on
1413 # failure unless no_fallback_to_full is specified.
1414 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001415 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001416 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001417 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001418 UNZIP_PATTERN)
Tao Bao767e3ac2015-11-10 12:19:19 -08001419 OPTIONS.target_info_dict = OPTIONS.info_dict
1420 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1421 OPTIONS.source_tmp)
1422 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001423 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001424 common.DumpInfoDict(OPTIONS.source_info_dict)
1425 try:
Tao Bao457cbf62017-03-06 09:56:01 -08001426 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001427 if OPTIONS.log_diff:
1428 out_file = open(OPTIONS.log_diff, 'w')
1429 import target_files_diff
1430 target_files_diff.recursiveDiff('',
1431 OPTIONS.source_tmp,
1432 OPTIONS.input_tmp,
1433 out_file)
1434 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001435 except ValueError:
1436 if not OPTIONS.fallback_to_full:
1437 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08001438 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08001439 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001440 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001441
Tao Bao767e3ac2015-11-10 12:19:19 -08001442 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001443
Tao Bao767e3ac2015-11-10 12:19:19 -08001444 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001445 if not OPTIONS.no_signing:
1446 SignOutput(temp_zip_file.name, args[1])
1447 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001448
Tao Bao89fbb0f2017-01-10 10:47:58 -08001449 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001450
1451
1452if __name__ == '__main__':
1453 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001454 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001455 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001456 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001457 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001458 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001459 finally:
1460 common.Cleanup()