blob: c3d742e5250aa8f4451cb5fcebf0007fa232a050 [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
Tao Bao481bab82017-12-21 11:23:09 -080052 properties on the OEM partition of the intended device. Multiple expected
53 values can be used by providing multiple files. Only the first dict will
54 be used to compute fingerprint, while the rest will be used to assert
55 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080056
Tao Bao8608cde2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Tao Bao337633f2017-12-06 15:20:19 -080063 --wipe_user_data
Doug Zongkerdbfaae52009-04-21 17:12:54 -070064 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Tao Bao5d182562016-02-23 11:38:39 -080067 --downgrade
68 Intentionally generate an incremental OTA that updates from a newer
69 build to an older one (based on timestamp comparison). "post-timestamp"
70 will be replaced by "ota-downgrade=yes" in the metadata file. A data
71 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080072 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080073 the OTA package, unless --binary flag is specified. Please also check the
74 doc for --override_timestamp below.
75
76 --override_timestamp
77 Intentionally generate an incremental OTA that updates from a newer
78 build to an older one (based on timestamp comparison), by overriding the
79 timestamp in package metadata. This differs from --downgrade flag: we
80 know for sure this is NOT an actual downgrade case, but two builds are
81 cut in a reverse order. A legit use case is that we cut a new build C
82 (after having A and B), but want to enfore an update path of A -> C -> B.
83 Specifying --downgrade may not help since that would enforce a data wipe
84 for C -> B update. The value of "post-timestamp" will be set to the newer
85 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080086
Doug Zongker1c390a22009-05-14 19:06:36 -070087 -e (--extra_script) <file>
88 Insert the contents of file at the end of the update script.
89
Doug Zongker9b23f2c2013-11-25 14:44:12 -080090 -2 (--two_step)
91 Generate a 'two-step' OTA package, where recovery is updated
92 first, so that any changes made to the system partition are done
93 using the new recovery (new kernel, etc.).
94
Doug Zongker26e66192014-02-20 13:22:07 -080095 --block
Tao Bao457cbf62017-03-06 09:56:01 -080096 Generate a block-based OTA for non-A/B device. We have deprecated the
97 support for file-based OTA since O. Block-based OTA will be used by
98 default for all non-A/B devices. Keeping this flag here to not break
99 existing callers.
Doug Zongker26e66192014-02-20 13:22:07 -0800100
Doug Zongker25568482014-03-03 10:21:27 -0800101 -b (--binary) <file>
102 Use the given binary as the update-binary in the output package,
103 instead of the binary in the build's target_files. Use for
104 development only.
105
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200106 -t (--worker_threads) <int>
107 Specifies the number of worker-threads that will be used when
108 generating patches for incremental updates (defaults to 3).
109
Tao Bao8dcf7382015-05-21 14:09:49 -0700110 --stash_threshold <float>
111 Specifies the threshold that will be used to compute the maximum
112 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800113
Tao Baod62c6032015-11-30 09:40:20 -0800114 --log_diff <file>
115 Generate a log file that shows the differences in the source and target
116 builds for an incremental package. This option is only meaningful when
117 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700118
119 --payload_signer <signer>
120 Specify the signer when signing the payload and metadata for A/B OTAs.
121 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
122 with the package private key. If the private key cannot be accessed
123 directly, a payload signer that knows how to do that should be specified.
124 The signer will be supplied with "-inkey <path_to_key>",
125 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700126
127 --payload_signer_args <args>
128 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700129"""
130
Tao Bao89fbb0f2017-01-10 10:47:58 -0800131from __future__ import print_function
132
Doug Zongkerfc44a512014-08-26 13:10:25 -0700133import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800134import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700135import shlex
Tao Bao481bab82017-12-21 11:23:09 -0800136import subprocess
137import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700138import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700139import zipfile
140
141import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700142import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700143import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700144
Tao Bao481bab82017-12-21 11:23:09 -0800145if sys.hexversion < 0x02070000:
146 print("Python 2.7 or newer is required.", file=sys.stderr)
147 sys.exit(1)
148
149
Doug Zongkereef39442009-04-02 12:14:19 -0700150OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700151OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700152OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700153OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700154OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700155OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800156OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800157OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700158OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700159OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
160if OPTIONS.worker_threads == 0:
161 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800162OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900163OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800164OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800165OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700166OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800167OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700168OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700169OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700170# Stash size cannot exceed cache_size * threshold.
171OPTIONS.cache_size = None
172OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800173OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700174OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700175OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700176OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200177OPTIONS.key_passwords = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700178
Tao Bao2dd1c482017-02-03 16:49:39 -0800179METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800180UNZIP_PATTERN = ['IMAGES/*', 'META/*']
181
Tao Bao2dd1c482017-02-03 16:49:39 -0800182
Tao Bao481bab82017-12-21 11:23:09 -0800183class BuildInfo(object):
184 """A class that holds the information for a given build.
185
186 This class wraps up the property querying for a given source or target build.
187 It abstracts away the logic of handling OEM-specific properties, and caches
188 the commonly used properties such as fingerprint.
189
190 There are two types of info dicts: a) build-time info dict, which is generated
191 at build time (i.e. included in a target_files zip); b) OEM info dict that is
192 specified at package generation time (via command line argument
193 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
194 having "oem_fingerprint_properties" in build-time info dict), all the queries
195 would be answered based on build-time info dict only. Otherwise if using
196 OEM-specific properties, some of them will be calculated from two info dicts.
197
198 Users can query properties similarly as using a dict() (e.g. info['fstab']),
199 or to query build properties via GetBuildProp() or GetVendorBuildProp().
200
201 Attributes:
202 info_dict: The build-time info dict.
203 is_ab: Whether it's a build that uses A/B OTA.
204 oem_dicts: A list of OEM dicts.
205 oem_props: A list of OEM properties that should be read from OEM dicts; None
206 if the build doesn't use any OEM-specific property.
207 fingerprint: The fingerprint of the build, which would be calculated based
208 on OEM properties if applicable.
209 device: The device name, which could come from OEM dicts if applicable.
210 """
211
212 def __init__(self, info_dict, oem_dicts):
213 """Initializes a BuildInfo instance with the given dicts.
214
215 Arguments:
216 info_dict: The build-time info dict.
217 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
218 that it always uses the first dict to calculate the fingerprint or the
219 device name. The rest would be used for asserting OEM properties only
220 (e.g. one package can be installed on one of these devices).
221 """
222 self.info_dict = info_dict
223 self.oem_dicts = oem_dicts
224
225 self._is_ab = info_dict.get("ab_update") == "true"
226 self._oem_props = info_dict.get("oem_fingerprint_properties")
227
228 if self._oem_props:
229 assert oem_dicts, "OEM source required for this build"
230
231 # These two should be computed only after setting self._oem_props.
232 self._device = self.GetOemProperty("ro.product.device")
233 self._fingerprint = self.CalculateFingerprint()
234
235 @property
236 def is_ab(self):
237 return self._is_ab
238
239 @property
240 def device(self):
241 return self._device
242
243 @property
244 def fingerprint(self):
245 return self._fingerprint
246
247 @property
248 def oem_props(self):
249 return self._oem_props
250
251 def __getitem__(self, key):
252 return self.info_dict[key]
253
254 def get(self, key, default=None):
255 return self.info_dict.get(key, default)
256
257 def GetBuildProp(self, prop):
258 """Returns the inquired build property."""
259 try:
260 return self.info_dict.get("build.prop", {})[prop]
261 except KeyError:
262 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
263
264 def GetVendorBuildProp(self, prop):
265 """Returns the inquired vendor build property."""
266 try:
267 return self.info_dict.get("vendor.build.prop", {})[prop]
268 except KeyError:
269 raise common.ExternalError(
270 "couldn't find %s in vendor.build.prop" % (prop,))
271
272 def GetOemProperty(self, key):
273 if self.oem_props is not None and key in self.oem_props:
274 return self.oem_dicts[0][key]
275 return self.GetBuildProp(key)
276
277 def CalculateFingerprint(self):
278 if self.oem_props is None:
279 return self.GetBuildProp("ro.build.fingerprint")
280 return "%s/%s/%s:%s" % (
281 self.GetOemProperty("ro.product.brand"),
282 self.GetOemProperty("ro.product.name"),
283 self.GetOemProperty("ro.product.device"),
284 self.GetBuildProp("ro.build.thumbprint"))
285
286 def WriteMountOemScript(self, script):
287 assert self.oem_props is not None
288 recovery_mount_options = self.info_dict.get("recovery_mount_options")
289 script.Mount("/oem", recovery_mount_options)
290
291 def WriteDeviceAssertions(self, script, oem_no_mount):
292 # Read the property directly if not using OEM properties.
293 if not self.oem_props:
294 script.AssertDevice(self.device)
295 return
296
297 # Otherwise assert OEM properties.
298 if not self.oem_dicts:
299 raise common.ExternalError(
300 "No OEM file provided to answer expected assertions")
301
302 for prop in self.oem_props.split():
303 values = []
304 for oem_dict in self.oem_dicts:
305 if prop in oem_dict:
306 values.append(oem_dict[prop])
307 if not values:
308 raise common.ExternalError(
309 "The OEM file is missing the property %s" % (prop,))
310 script.AssertOemProperty(prop, values, oem_no_mount)
311
312
Tao Baofabe0832018-01-17 15:52:28 -0800313class PayloadSigner(object):
314 """A class that wraps the payload signing works.
315
316 When generating a Payload, hashes of the payload and metadata files will be
317 signed with the device key, either by calling an external payload signer or
318 by calling openssl with the package key. This class provides a unified
319 interface, so that callers can just call PayloadSigner.Sign().
320
321 If an external payload signer has been specified (OPTIONS.payload_signer), it
322 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
323 that the signing key should be provided as part of the payload_signer_args.
324 Otherwise without an external signer, it uses the package key
325 (OPTIONS.package_key) and calls openssl for the signing works.
326 """
327
328 def __init__(self):
329 if OPTIONS.payload_signer is None:
330 # Prepare the payload signing key.
331 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
332 pw = OPTIONS.key_passwords[OPTIONS.package_key]
333
334 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
335 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
336 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
337 cmd.extend(["-out", signing_key])
338
339 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
340 stderr=subprocess.STDOUT)
341 stdoutdata, _ = get_signing_key.communicate()
342 assert get_signing_key.returncode == 0, \
343 "Failed to get signing key: {}".format(stdoutdata)
344
345 self.signer = "openssl"
346 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
347 "-pkeyopt", "digest:sha256"]
348 else:
349 self.signer = OPTIONS.payload_signer
350 self.signer_args = OPTIONS.payload_signer_args
351
352 def Sign(self, in_file):
353 """Signs the given input file. Returns the output filename."""
354 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
355 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
356 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
357 stdoutdata, _ = signing.communicate()
358 assert signing.returncode == 0, \
359 "Failed to sign the input file: {}".format(stdoutdata)
360 return out_file
361
362
Doug Zongkereef39442009-04-02 12:14:19 -0700363def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200364 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700365
Doug Zongker951495f2009-08-14 12:44:19 -0700366 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
367 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700368
369
Tao Bao481bab82017-12-21 11:23:09 -0800370def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800371 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800372 if not oem_source:
373 return None
374
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800375 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800376 for oem_file in oem_source:
377 with open(oem_file) as fp:
378 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800379 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700380
Doug Zongkereef39442009-04-02 12:14:19 -0700381
Tao Baod42e97e2016-11-30 12:11:57 -0800382def _WriteRecoveryImageToBoot(script, output_zip):
383 """Find and write recovery image to /boot in two-step OTA.
384
385 In two-step OTAs, we write recovery image to /boot as the first step so that
386 we can reboot to there and install a new recovery image to /recovery.
387 A special "recovery-two-step.img" will be preferred, which encodes the correct
388 path of "/boot". Otherwise the device may show "device is corrupt" message
389 when booting into /boot.
390
391 Fall back to using the regular recovery.img if the two-step recovery image
392 doesn't exist. Note that rebuilding the special image at this point may be
393 infeasible, because we don't have the desired boot signer and keys when
394 calling ota_from_target_files.py.
395 """
396
397 recovery_two_step_img_name = "recovery-two-step.img"
398 recovery_two_step_img_path = os.path.join(
399 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
400 if os.path.exists(recovery_two_step_img_path):
401 recovery_two_step_img = common.GetBootableImage(
402 recovery_two_step_img_name, recovery_two_step_img_name,
403 OPTIONS.input_tmp, "RECOVERY")
404 common.ZipWriteStr(
405 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800406 print("two-step package: using %s in stage 1/3" % (
407 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800408 script.WriteRawImage("/boot", recovery_two_step_img_name)
409 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800410 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800411 # The "recovery.img" entry has been written into package earlier.
412 script.WriteRawImage("/boot", "recovery.img")
413
414
Doug Zongkerc9253822014-02-04 12:17:58 -0800415def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700416 namelist = [name for name in target_files_zip.namelist()]
417 return ("SYSTEM/recovery-from-boot.p" in namelist or
418 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700419
Tao Bao457cbf62017-03-06 09:56:01 -0800420
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700421def HasVendorPartition(target_files_zip):
422 try:
423 target_files_zip.getinfo("VENDOR/")
424 return True
425 except KeyError:
426 return False
427
Tao Bao457cbf62017-03-06 09:56:01 -0800428
Tao Bao481bab82017-12-21 11:23:09 -0800429def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700430 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800431 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700432
433
Tao Bao481bab82017-12-21 11:23:09 -0800434def WriteFingerprintAssertion(script, target_info, source_info):
435 source_oem_props = source_info.oem_props
436 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700437
Tao Bao481bab82017-12-21 11:23:09 -0800438 if source_oem_props is None and target_oem_props is None:
439 script.AssertSomeFingerprint(
440 source_info.fingerprint, target_info.fingerprint)
441 elif source_oem_props is not None and target_oem_props is not None:
442 script.AssertSomeThumbprint(
443 target_info.GetBuildProp("ro.build.thumbprint"),
444 source_info.GetBuildProp("ro.build.thumbprint"))
445 elif source_oem_props is None and target_oem_props is not None:
446 script.AssertFingerprintOrThumbprint(
447 source_info.fingerprint,
448 target_info.GetBuildProp("ro.build.thumbprint"))
449 else:
450 script.AssertFingerprintOrThumbprint(
451 target_info.fingerprint,
452 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700453
Doug Zongkerfc44a512014-08-26 13:10:25 -0700454
Tao Bao7e0f1602017-03-06 15:50:08 -0800455def GetImage(which, tmpdir):
456 """Returns an image object suitable for passing to BlockImageDiff.
457
458 'which' partition must be "system" or "vendor". A prebuilt image and file
459 map must already exist in tmpdir.
460 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700461
462 assert which in ("system", "vendor")
463
464 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700465 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700466
Tao Bao7e0f1602017-03-06 15:50:08 -0800467 # The image and map files must have been created prior to calling
468 # ota_from_target_files.py (since LMP).
469 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700470
Tao Baoff777812015-05-12 11:42:31 -0700471 # Bug: http://b/20939131
472 # In ext4 filesystems, block 0 might be changed even being mounted
473 # R/O. We add it to clobbered_blocks so that it will be written to the
474 # target unconditionally. Note that they are still part of care_map.
475 clobbered_blocks = "0"
476
477 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700478
479
Tao Bao481bab82017-12-21 11:23:09 -0800480def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
481 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700482 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700483
484 Metadata used for on-device compatibility verification is retrieved from
485 target_zip then added to compatibility.zip which is added to the output_zip
486 archive.
487
Tao Baobcd1d162017-08-26 13:10:26 -0700488 Compatibility archive should only be included for devices that have enabled
489 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700490
491 Args:
492 target_zip: Zip file containing the source files to be included for OTA.
493 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800494 target_info: The BuildInfo instance that holds the target build info.
495 source_info: The BuildInfo instance that holds the source build info, if
496 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700497 """
498
Tao Baobcd1d162017-08-26 13:10:26 -0700499 def AddCompatibilityArchive(system_updated, vendor_updated):
500 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700501
Tao Baobcd1d162017-08-26 13:10:26 -0700502 Args:
503 system_updated: If True, the system image will be updated and therefore
504 its metadata should be included.
505 vendor_updated: If True, the vendor image will be updated and therefore
506 its metadata should be included.
507 """
508 # Determine what metadata we need. Files are names relative to META/.
509 compatibility_files = []
510 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
511 system_metadata = ("system_manifest.xml", "system_matrix.xml")
512 if vendor_updated:
513 compatibility_files += vendor_metadata
514 if system_updated:
515 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700516
Tao Baobcd1d162017-08-26 13:10:26 -0700517 # Create new archive.
518 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800519 compatibility_archive_zip = zipfile.ZipFile(
520 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700521
Tao Baobcd1d162017-08-26 13:10:26 -0700522 # Add metadata.
523 for file_name in compatibility_files:
524 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700525
Tao Baobcd1d162017-08-26 13:10:26 -0700526 if target_file_name in target_zip.namelist():
527 data = target_zip.read(target_file_name)
528 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700529
Tao Baobcd1d162017-08-26 13:10:26 -0700530 # Ensure files are written before we copy into output_zip.
531 compatibility_archive_zip.close()
532
533 # Only add the archive if we have any compatibility info.
534 if compatibility_archive_zip.namelist():
535 common.ZipWrite(output_zip, compatibility_archive.name,
536 arcname="compatibility.zip",
537 compress_type=zipfile.ZIP_STORED)
538
539 # Will only proceed if the target has enabled the Treble support (as well as
540 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800541 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700542 return
543
544 # We don't support OEM thumbprint in Treble world (which calculates
545 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800546 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700547
548 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800549 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700550 AddCompatibilityArchive(True, True)
551 return
552
Tao Bao481bab82017-12-21 11:23:09 -0800553 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700554
Tao Bao481bab82017-12-21 11:23:09 -0800555 source_fp = source_info.fingerprint
556 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700557 system_updated = source_fp != target_fp
558
Tao Bao481bab82017-12-21 11:23:09 -0800559 source_fp_vendor = source_info.GetVendorBuildProp(
560 "ro.vendor.build.fingerprint")
561 target_fp_vendor = target_info.GetVendorBuildProp(
562 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700563 vendor_updated = source_fp_vendor != target_fp_vendor
564
565 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700566
567
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700568def WriteFullOTAPackage(input_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800569 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700570
Tao Bao481bab82017-12-21 11:23:09 -0800571 # We don't know what version it will be installed on top of. We expect the API
572 # just won't change very often. Similarly for fstab, it might have changed in
573 # the target build.
574 target_api_version = target_info["recovery_api_version"]
575 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700576
Tao Bao481bab82017-12-21 11:23:09 -0800577 if target_info.oem_props and not OPTIONS.oem_no_mount:
578 target_info.WriteMountOemScript(script)
579
Tao Baodf3a48b2018-01-10 16:30:43 -0800580 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700581
Doug Zongker05d3dea2009-06-22 11:32:31 -0700582 device_specific = common.DeviceSpecificParams(
583 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800584 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700585 output_zip=output_zip,
586 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700587 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700588 metadata=metadata,
589 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700590
Tao Bao457cbf62017-03-06 09:56:01 -0800591 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800592
Tao Bao481bab82017-12-21 11:23:09 -0800593 # Assertions (e.g. downgrade check, device properties check).
594 ts = target_info.GetBuildProp("ro.build.date.utc")
595 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700596 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700597
Tao Bao481bab82017-12-21 11:23:09 -0800598 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700599 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800600
601 # Two-step package strategy (in chronological order, which is *not*
602 # the order in which the generated script has things):
603 #
604 # if stage is not "2/3" or "3/3":
605 # write recovery image to boot partition
606 # set stage to "2/3"
607 # reboot to boot partition and restart recovery
608 # else if stage is "2/3":
609 # write recovery image to recovery partition
610 # set stage to "3/3"
611 # reboot to recovery partition and restart recovery
612 # else:
613 # (stage must be "3/3")
614 # set stage to ""
615 # do normal full package installation:
616 # wipe and install system, boot image, etc.
617 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700618 # complete script normally
619 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800620
621 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
622 OPTIONS.input_tmp, "RECOVERY")
623 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800624 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800625 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800626 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800627 assert fs.fs_type.upper() == "EMMC", \
628 "two-step packages only supported on devices with EMMC /misc partitions"
629 bcb_dev = {"bcb_dev": fs.device}
630 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
631 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700632if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800633""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800634
635 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
636 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800637 script.WriteRawImage("/recovery", "recovery.img")
638 script.AppendExtra("""
639set_stage("%(bcb_dev)s", "3/3");
640reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700641else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800642""" % bcb_dev)
643
Tao Baod42e97e2016-11-30 12:11:57 -0800644 # Stage 3/3: Make changes.
645 script.Comment("Stage 3/3")
646
Tao Bao6c55a8a2015-04-08 15:30:27 -0700647 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800648 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700649
Doug Zongkere5ff5902012-01-17 10:55:37 -0800650 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700651
Doug Zongker01ce19c2014-02-04 13:48:15 -0800652 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700653
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700654 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800655 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700656 if HasVendorPartition(input_zip):
657 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700658
Doug Zongker4b9596f2014-06-09 14:15:45 -0700659 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800660
Tao Bao457cbf62017-03-06 09:56:01 -0800661 # Full OTA is done as an "incremental" against an empty source image. This
662 # has the effect of writing new data from the package to the entire
663 # partition, but lets us reuse the updater code that writes incrementals to
664 # do it.
665 system_tgt = GetImage("system", OPTIONS.input_tmp)
666 system_tgt.ResetFileMap()
667 system_diff = common.BlockDifference("system", system_tgt, src=None)
668 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700669
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700670 boot_img = common.GetBootableImage(
671 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800672
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700673 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700674 script.ShowProgress(0.1, 0)
675
Tao Bao457cbf62017-03-06 09:56:01 -0800676 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
677 vendor_tgt.ResetFileMap()
678 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
679 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700680
Tao Bao481bab82017-12-21 11:23:09 -0800681 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700682
Tao Bao481bab82017-12-21 11:23:09 -0800683 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700684 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700685
Doug Zongker01ce19c2014-02-04 13:48:15 -0800686 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700687 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700688
Doug Zongker01ce19c2014-02-04 13:48:15 -0800689 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700690 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700691
Doug Zongker1c390a22009-05-14 19:06:36 -0700692 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700693 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700694
Doug Zongker14833602010-02-02 13:12:04 -0800695 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800696
Doug Zongker922206e2014-03-04 13:16:24 -0800697 if OPTIONS.wipe_user_data:
698 script.ShowProgress(0.1, 10)
699 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700700
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800701 if OPTIONS.two_step:
702 script.AppendExtra("""
703set_stage("%(bcb_dev)s", "");
704""" % bcb_dev)
705 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800706
707 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
708 script.Comment("Stage 1/3")
709 _WriteRecoveryImageToBoot(script, output_zip)
710
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800711 script.AppendExtra("""
712set_stage("%(bcb_dev)s", "2/3");
713reboot_now("%(bcb_dev)s", "");
714endif;
715endif;
716""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800717
Tao Bao5d182562016-02-23 11:38:39 -0800718 script.SetProgress(1)
719 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800720 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700721 WriteMetadata(metadata, output_zip)
722
Doug Zongkerfc44a512014-08-26 13:10:25 -0700723
Doug Zongker2ea21062010-04-28 16:05:21 -0700724def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800725 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
726 common.ZipWriteStr(output_zip, METADATA_NAME, value,
727 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700728
Doug Zongkerfc44a512014-08-26 13:10:25 -0700729
Tao Bao481bab82017-12-21 11:23:09 -0800730def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800731 # Only incremental OTAs are allowed to reach here.
732 assert OPTIONS.incremental_source is not None
733
Tao Bao481bab82017-12-21 11:23:09 -0800734 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
735 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800736 is_downgrade = long(post_timestamp) < long(pre_timestamp)
737
738 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800739 if not is_downgrade:
740 raise RuntimeError("--downgrade specified but no downgrade detected: "
741 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800742 metadata["ota-downgrade"] = "yes"
743 elif OPTIONS.timestamp:
744 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800745 raise RuntimeError("--override_timestamp specified but no timestamp hack "
746 "needed: pre: %s, post: %s" % (pre_timestamp,
747 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800748 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800749 else:
750 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800751 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800752 "pre: %s, post: %s. Need to specify "
753 "--override_timestamp OR --downgrade to allow "
754 "building the incremental." % (pre_timestamp,
755 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800756 metadata["post-timestamp"] = post_timestamp
757
758
Tao Baodf3a48b2018-01-10 16:30:43 -0800759def GetPackageMetadata(target_info, source_info=None):
760 """Generates and returns the metadata dict.
761
762 It generates a dict() that contains the info to be written into an OTA
763 package (META-INF/com/android/metadata). It also handles the detection of
764 downgrade / timestamp override / data wipe based on the global options.
765
766 Args:
767 target_info: The BuildInfo instance that holds the target build info.
768 source_info: The BuildInfo instance that holds the source build info, or
769 None if generating full OTA.
770
771 Returns:
772 A dict to be written into package metadata entry.
773 """
774 assert isinstance(target_info, BuildInfo)
775 assert source_info is None or isinstance(source_info, BuildInfo)
776
777 metadata = {
778 'post-build' : target_info.fingerprint,
779 'post-build-incremental' : target_info.GetBuildProp(
780 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800781 'post-sdk-level' : target_info.GetBuildProp(
782 'ro.build.version.sdk'),
783 'post-security-patch-level' : target_info.GetBuildProp(
784 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800785 }
786
787 if target_info.is_ab:
788 metadata['ota-type'] = 'AB'
789 metadata['ota-required-cache'] = '0'
790 else:
791 metadata['ota-type'] = 'BLOCK'
792
793 if OPTIONS.wipe_user_data:
794 metadata['ota-wipe'] = 'yes'
795
796 is_incremental = source_info is not None
797 if is_incremental:
798 metadata['pre-build'] = source_info.fingerprint
799 metadata['pre-build-incremental'] = source_info.GetBuildProp(
800 'ro.build.version.incremental')
801 metadata['pre-device'] = source_info.device
802 else:
803 metadata['pre-device'] = target_info.device
804
805 # Detect downgrades, or fill in the post-timestamp.
806 if is_incremental:
807 HandleDowngradeMetadata(metadata, target_info, source_info)
808 else:
809 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
810
811 return metadata
812
813
Geremy Condra36bd3652014-02-06 19:45:10 -0800814def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800815 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
816 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800817
Tao Bao481bab82017-12-21 11:23:09 -0800818 target_api_version = target_info["recovery_api_version"]
819 source_api_version = source_info["recovery_api_version"]
820 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700821 print("WARNING: generating edify script for a source that "
822 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800823
Tao Bao481bab82017-12-21 11:23:09 -0800824 script = edify_generator.EdifyGenerator(
825 source_api_version, target_info, fstab=source_info["fstab"])
826
827 if target_info.oem_props or source_info.oem_props:
828 if not OPTIONS.oem_no_mount:
829 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -0700830
Tao Baodf3a48b2018-01-10 16:30:43 -0800831 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -0800832
Geremy Condra36bd3652014-02-06 19:45:10 -0800833 device_specific = common.DeviceSpecificParams(
834 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800835 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800836 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800837 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800838 output_zip=output_zip,
839 script=script,
840 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -0800841 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800842
Geremy Condra36bd3652014-02-06 19:45:10 -0800843 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800844 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800845 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800846 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800847 updating_boot = (not OPTIONS.two_step and
848 (source_boot.data != target_boot.data))
849
Geremy Condra36bd3652014-02-06 19:45:10 -0800850 target_recovery = common.GetBootableImage(
851 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800852
Tao Bao7e0f1602017-03-06 15:50:08 -0800853 system_src = GetImage("system", OPTIONS.source_tmp)
854 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700855
Tao Bao0582cb62017-12-21 11:47:01 -0800856 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -0800857 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -0800858 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -0700859
Tao Baof8acad12016-07-07 09:09:58 -0700860 # Check the first block of the source system partition for remount R/W only
861 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -0800862 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700863 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700864 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
865 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
866 # b) the blocks listed in block map may not contain all the bytes for a given
867 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -0800868 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700869 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
870 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700871 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800872 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700873 version=blockimgdiff_version,
874 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700875
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700876 if HasVendorPartition(target_zip):
877 if not HasVendorPartition(source_zip):
878 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800879 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
880 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800881
882 # Check first block of vendor partition for remount R/W only if
883 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -0800884 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800885 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700886 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700887 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800888 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700889 version=blockimgdiff_version,
890 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700891 else:
892 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800893
Tao Baobcd1d162017-08-26 13:10:26 -0700894 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -0800895 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700896
Tao Bao481bab82017-12-21 11:23:09 -0800897 # Assertions (e.g. device properties check).
898 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -0800899 device_specific.IncrementalOTA_Assertions()
900
901 # Two-step incremental package strategy (in chronological order,
902 # which is *not* the order in which the generated script has
903 # things):
904 #
905 # if stage is not "2/3" or "3/3":
906 # do verification on current system
907 # write recovery image to boot partition
908 # set stage to "2/3"
909 # reboot to boot partition and restart recovery
910 # else if stage is "2/3":
911 # write recovery image to recovery partition
912 # set stage to "3/3"
913 # reboot to recovery partition and restart recovery
914 # else:
915 # (stage must be "3/3")
916 # perform update:
917 # patch system files, etc.
918 # force full install of new boot image
919 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700920 # complete script normally
921 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800922
923 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800924 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -0800925 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700926 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800927 assert fs.fs_type.upper() == "EMMC", \
928 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -0800929 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -0800930 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
931 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700932if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800933""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800934
935 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
936 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700937 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800938 script.WriteRawImage("/recovery", "recovery.img")
939 script.AppendExtra("""
940set_stage("%(bcb_dev)s", "3/3");
941reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700942else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800943""" % bcb_dev)
944
Tao Baod42e97e2016-11-30 12:11:57 -0800945 # Stage 1/3: (a) Verify the current system.
946 script.Comment("Stage 1/3")
947
Tao Bao6c55a8a2015-04-08 15:30:27 -0700948 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800949 script.Print("Source: {}".format(source_info.fingerprint))
950 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700951
Geremy Condra36bd3652014-02-06 19:45:10 -0800952 script.Print("Verifying current system...")
953
954 device_specific.IncrementalOTA_VerifyBegin()
955
Tao Bao481bab82017-12-21 11:23:09 -0800956 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800957
Tao Baod8d14be2016-02-04 14:26:02 -0800958 # Check the required cache size (i.e. stashed blocks).
959 size = []
960 if system_diff:
961 size.append(system_diff.required_cache)
962 if vendor_diff:
963 size.append(vendor_diff.required_cache)
964
Geremy Condra36bd3652014-02-06 19:45:10 -0800965 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -0800966 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800967 d = common.Difference(target_boot, source_boot)
968 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700969 if d is None:
970 include_full_boot = True
971 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
972 else:
973 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800974
Tao Bao89fbb0f2017-01-10 10:47:58 -0800975 print("boot target: %d source: %d diff: %d" % (
976 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800977
Doug Zongkerf8340082014-08-05 10:39:37 -0700978 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800979
Doug Zongkerf8340082014-08-05 10:39:37 -0700980 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
981 (boot_type, boot_device,
982 source_boot.size, source_boot.sha1,
983 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800984 size.append(target_boot.size)
985
986 if size:
987 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800988
989 device_specific.IncrementalOTA_VerifyEnd()
990
991 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800992 # Stage 1/3: (b) Write recovery image to /boot.
993 _WriteRecoveryImageToBoot(script, output_zip)
994
Geremy Condra36bd3652014-02-06 19:45:10 -0800995 script.AppendExtra("""
996set_stage("%(bcb_dev)s", "2/3");
997reboot_now("%(bcb_dev)s", "");
998else
999""" % bcb_dev)
1000
Tao Baod42e97e2016-11-30 12:11:57 -08001001 # Stage 3/3: Make changes.
1002 script.Comment("Stage 3/3")
1003
Jesse Zhao75bcea02015-01-06 10:59:53 -08001004 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001005 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001006 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001007 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001008
Geremy Condra36bd3652014-02-06 19:45:10 -08001009 script.Comment("---- start making changes here ----")
1010
1011 device_specific.IncrementalOTA_InstallBegin()
1012
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001013 system_diff.WriteScript(script, output_zip,
1014 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001015
Doug Zongkerfc44a512014-08-26 13:10:25 -07001016 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001017 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001018
1019 if OPTIONS.two_step:
1020 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1021 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001022 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001023
1024 if not OPTIONS.two_step:
1025 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001026 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001027 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001028 script.Print("Installing boot image...")
1029 script.WriteRawImage("/boot", "boot.img")
1030 else:
1031 # Produce the boot image by applying a patch to the current
1032 # contents of the boot partition, and write it back to the
1033 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001034 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001035 script.Print("Patching boot image...")
1036 script.ShowProgress(0.1, 10)
1037 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1038 % (boot_type, boot_device,
1039 source_boot.size, source_boot.sha1,
1040 target_boot.size, target_boot.sha1),
1041 "-",
1042 target_boot.size, target_boot.sha1,
1043 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001044 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001045 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001046
1047 # Do device-specific installation (eg, write radio image).
1048 device_specific.IncrementalOTA_InstallEnd()
1049
1050 if OPTIONS.extra_script is not None:
1051 script.AppendExtra(OPTIONS.extra_script)
1052
Doug Zongker922206e2014-03-04 13:16:24 -08001053 if OPTIONS.wipe_user_data:
1054 script.Print("Erasing user data...")
1055 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001056 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001057
Geremy Condra36bd3652014-02-06 19:45:10 -08001058 if OPTIONS.two_step:
1059 script.AppendExtra("""
1060set_stage("%(bcb_dev)s", "");
1061endif;
1062endif;
1063""" % bcb_dev)
1064
1065 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001066 # For downgrade OTAs, we prefer to use the update-binary in the source
1067 # build that is actually newer than the one in the target build.
1068 if OPTIONS.downgrade:
1069 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1070 else:
1071 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001072 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001073 WriteMetadata(metadata, output_zip)
1074
Doug Zongker32b527d2014-03-04 10:03:02 -08001075
Tao Baoc098e9e2016-01-07 13:03:56 -08001076def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1077 source_file=None):
1078 """Generate an Android OTA package that has A/B update payload."""
1079
Tao Bao2dd1c482017-02-03 16:49:39 -08001080 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1081 expected_length=None):
1082 """Compute the streaming metadata for a given zip.
1083
1084 When 'reserve_space' is True, we reserve extra space for the offset and
1085 length of the metadata entry itself, although we don't know the final
1086 values until the package gets signed. This function will be called again
1087 after signing. We then write the actual values and pad the string to the
1088 length we set earlier. Note that we can't use the actual length of the
1089 metadata entry in the second run. Otherwise the offsets for other entries
1090 will be changing again.
1091 """
Tao Baoc96316c2017-01-24 22:10:49 -08001092
1093 def ComputeEntryOffsetSize(name):
1094 """Compute the zip entry offset and size."""
1095 info = zip_file.getinfo(name)
1096 offset = info.header_offset + len(info.FileHeader())
1097 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001098 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001099
1100 # payload.bin and payload_properties.txt must exist.
1101 offsets = [ComputeEntryOffsetSize('payload.bin'),
1102 ComputeEntryOffsetSize('payload_properties.txt')]
1103
1104 # care_map.txt is available only if dm-verity is enabled.
1105 if 'care_map.txt' in zip_file.namelist():
1106 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001107
Tao Bao21803d32017-04-19 10:16:09 -07001108 if 'compatibility.zip' in zip_file.namelist():
1109 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1110
Tao Bao2dd1c482017-02-03 16:49:39 -08001111 # 'META-INF/com/android/metadata' is required. We don't know its actual
1112 # offset and length (as well as the values for other entries). So we
1113 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1114 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1115 # beginning of the zip), as well as the possible value changes in other
1116 # entries.
1117 if reserve_space:
1118 offsets.append('metadata:' + ' ' * 10)
1119 else:
1120 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1121
1122 value = ','.join(offsets)
1123 if expected_length is not None:
1124 assert len(value) <= expected_length, \
1125 'Insufficient reserved space: reserved=%d, actual=%d' % (
1126 expected_length, len(value))
1127 value += ' ' * (expected_length - len(value))
1128 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001129
Alex Deymod8d96ec2016-06-10 16:38:31 -07001130 # The place where the output from the subprocess should go.
1131 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1132
Tao Baofabe0832018-01-17 15:52:28 -08001133 # Get the PayloadSigner to be used in step 3.
1134 payload_signer = PayloadSigner()
Tao Baoc098e9e2016-01-07 13:03:56 -08001135
Tao Baodea0f8b2016-06-20 17:55:06 -07001136 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001137 temp_zip_file = tempfile.NamedTemporaryFile()
1138 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1139 compression=zipfile.ZIP_DEFLATED)
1140
Tao Bao481bab82017-12-21 11:23:09 -08001141 if source_file is not None:
1142 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1143 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1144 else:
1145 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1146 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001147
Tao Bao481bab82017-12-21 11:23:09 -08001148 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001149 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001150
Tao Baoc098e9e2016-01-07 13:03:56 -08001151 # 1. Generate payload.
1152 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1153 cmd = ["brillo_update_payload", "generate",
1154 "--payload", payload_file,
1155 "--target_image", target_file]
1156 if source_file is not None:
1157 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001158 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1159 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001160 assert p1.returncode == 0, "brillo_update_payload generate failed"
1161
1162 # 2. Generate hashes of the payload and metadata files.
1163 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1164 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1165 cmd = ["brillo_update_payload", "hash",
1166 "--unsigned_payload", payload_file,
1167 "--signature_size", "256",
1168 "--metadata_hash_file", metadata_sig_file,
1169 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001170 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1171 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001172 assert p1.returncode == 0, "brillo_update_payload hash failed"
1173
1174 # 3. Sign the hashes and insert them back into the payload file.
Tao Baoc098e9e2016-01-07 13:03:56 -08001175 # 3a. Sign the payload hash.
Tao Baofabe0832018-01-17 15:52:28 -08001176 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001177
1178 # 3b. Sign the metadata hash.
Tao Baofabe0832018-01-17 15:52:28 -08001179 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001180
1181 # 3c. Insert the signatures back into the payload file.
1182 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1183 suffix=".bin")
1184 cmd = ["brillo_update_payload", "sign",
1185 "--unsigned_payload", payload_file,
1186 "--payload", signed_payload_file,
1187 "--signature_size", "256",
1188 "--metadata_signature_file", signed_metadata_sig_file,
1189 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001190 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1191 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001192 assert p1.returncode == 0, "brillo_update_payload sign failed"
1193
Alex Deymo19241c12016-02-04 22:29:29 -08001194 # 4. Dump the signed payload properties.
1195 properties_file = common.MakeTempFile(prefix="payload-properties-",
1196 suffix=".txt")
1197 cmd = ["brillo_update_payload", "properties",
1198 "--payload", signed_payload_file,
1199 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001200 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1201 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001202 assert p1.returncode == 0, "brillo_update_payload properties failed"
1203
Tao Bao7c5dc572016-06-14 17:48:11 -07001204 if OPTIONS.wipe_user_data:
1205 with open(properties_file, "a") as f:
1206 f.write("POWERWASH=1\n")
1207 metadata["ota-wipe"] = "yes"
1208
Tao Baoc96316c2017-01-24 22:10:49 -08001209 # Add the signed payload file and properties into the zip. In order to
1210 # support streaming, we pack payload.bin, payload_properties.txt and
1211 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1212 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001213 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1214 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001215 common.ZipWrite(output_zip, properties_file,
1216 arcname="payload_properties.txt",
1217 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001218
Tianjie Xucfa86222016-03-07 16:31:19 -08001219 # If dm-verity is supported for the device, copy contents of care_map
1220 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001221 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001222 if (target_info.get("verity") == "true" or
1223 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001224 care_map_path = "META/care_map.txt"
1225 namelist = target_zip.namelist()
1226 if care_map_path in namelist:
1227 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001228 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001229 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001230 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001231 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001232
Tao Bao481bab82017-12-21 11:23:09 -08001233 # source_info must be None for full OTAs.
Tao Baobcd1d162017-08-26 13:10:26 -07001234 if source_file is None:
Tao Bao481bab82017-12-21 11:23:09 -08001235 assert source_info is None
Tao Bao21803d32017-04-19 10:16:09 -07001236
Tao Baobcd1d162017-08-26 13:10:26 -07001237 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001238 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001239
Tao Bao21803d32017-04-19 10:16:09 -07001240 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001241
Tao Bao2dd1c482017-02-03 16:49:39 -08001242 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001243 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001244 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001245 WriteMetadata(metadata, output_zip)
1246 common.ZipClose(output_zip)
1247
Tao Bao2dd1c482017-02-03 16:49:39 -08001248 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
Tao Bao89d7ab22017-12-14 17:05:33 -08001249 # ZIP entries, as well as padding the entry headers. We do a preliminary
Tao Bao2dd1c482017-02-03 16:49:39 -08001250 # signing (with an incomplete metadata entry) to allow that to happen. Then
Tao Bao89d7ab22017-12-14 17:05:33 -08001251 # compute the ZIP entry offsets, write back the final metadata and do the
Tao Bao2dd1c482017-02-03 16:49:39 -08001252 # final signing.
Tao Bao89d7ab22017-12-14 17:05:33 -08001253 prelim_signing = common.MakeTempFile(suffix='.zip')
1254 SignOutput(temp_zip_file.name, prelim_signing)
Tao Bao2dd1c482017-02-03 16:49:39 -08001255 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001256
Tao Bao2dd1c482017-02-03 16:49:39 -08001257 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Bao89d7ab22017-12-14 17:05:33 -08001258 prelim_signing_zip = zipfile.ZipFile(prelim_signing, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001259 expected_length = len(metadata['ota-streaming-property-files'])
1260 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao89d7ab22017-12-14 17:05:33 -08001261 prelim_signing_zip, reserve_space=False, expected_length=expected_length)
1262 common.ZipClose(prelim_signing_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001263
Tao Bao89d7ab22017-12-14 17:05:33 -08001264 # Replace the METADATA entry.
1265 common.ZipDelete(prelim_signing, METADATA_NAME)
1266 output_zip = zipfile.ZipFile(prelim_signing, 'a',
Tao Bao2dd1c482017-02-03 16:49:39 -08001267 compression=zipfile.ZIP_DEFLATED)
Tao Bao2dd1c482017-02-03 16:49:39 -08001268 WriteMetadata(metadata, output_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001269 common.ZipClose(output_zip)
1270
1271 # Re-sign the package after updating the metadata entry.
Tao Bao89d7ab22017-12-14 17:05:33 -08001272 SignOutput(prelim_signing, output_file)
Tao Bao2dd1c482017-02-03 16:49:39 -08001273
1274 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao89d7ab22017-12-14 17:05:33 -08001275 output_zip = zipfile.ZipFile(output_file, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001276 actual = metadata['ota-streaming-property-files'].strip()
1277 expected = ComputeStreamingMetadata(output_zip)
1278 assert actual == expected, \
1279 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001280 common.ZipClose(output_zip)
1281
Tao Baoc098e9e2016-01-07 13:03:56 -08001282
Doug Zongkereef39442009-04-02 12:14:19 -07001283def main(argv):
1284
1285 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001286 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001287 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001288 elif o in ("-i", "--incremental_from"):
1289 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001290 elif o == "--full_radio":
1291 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001292 elif o == "--full_bootloader":
1293 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001294 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001295 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001296 elif o == "--downgrade":
1297 OPTIONS.downgrade = True
1298 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001299 elif o == "--override_timestamp":
1300 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001301 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001302 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001303 elif o == "--oem_no_mount":
1304 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001305 elif o in ("-e", "--extra_script"):
1306 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001307 elif o in ("-t", "--worker_threads"):
1308 if a.isdigit():
1309 OPTIONS.worker_threads = int(a)
1310 else:
1311 raise ValueError("Cannot parse value %r for option %r - only "
1312 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001313 elif o in ("-2", "--two_step"):
1314 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001315 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001316 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001317 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001318 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001319 elif o == "--block":
1320 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001321 elif o in ("-b", "--binary"):
1322 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001323 elif o == "--stash_threshold":
1324 try:
1325 OPTIONS.stash_threshold = float(a)
1326 except ValueError:
1327 raise ValueError("Cannot parse value %r for option %r - expecting "
1328 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001329 elif o == "--log_diff":
1330 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001331 elif o == "--payload_signer":
1332 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001333 elif o == "--payload_signer_args":
1334 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001335 elif o == "--extracted_input_target_files":
1336 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001337 else:
1338 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001339 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001340
1341 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001342 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001343 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001344 "package_key=",
1345 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001346 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001347 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001348 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001349 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001350 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001351 "extra_script=",
1352 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001353 "two_step",
1354 "no_signing",
1355 "block",
1356 "binary=",
1357 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001358 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001359 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001360 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001361 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001362 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001363 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001364 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001365 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001366
1367 if len(args) != 2:
1368 common.Usage(__doc__)
1369 sys.exit(1)
1370
Tao Bao5d182562016-02-23 11:38:39 -08001371 if OPTIONS.downgrade:
1372 # Sanity check to enforce a data wipe.
1373 if not OPTIONS.wipe_user_data:
1374 raise ValueError("Cannot downgrade without a data wipe")
1375
1376 # We should only allow downgrading incrementals (as opposed to full).
1377 # Otherwise the device may go back from arbitrary build with this full
1378 # OTA package.
1379 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001380 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001381
Tao Bao3e6161a2017-02-28 11:48:48 -08001382 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1383 "Cannot have --downgrade AND --override_timestamp both"
1384
Tao Bao2db13852018-01-08 22:28:57 -08001385 # Load the build info dicts from the zip directly or the extracted input
1386 # directory. We don't need to unzip the entire target-files zips, because they
1387 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1388 # When loading the info dicts, we don't need to provide the second parameter
1389 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1390 # some properties with their actual paths, such as 'selinux_fc',
1391 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001392 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001393 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001394 else:
Tao Bao2db13852018-01-08 22:28:57 -08001395 with zipfile.ZipFile(args[0], 'r') as input_zip:
1396 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001397
Tao Bao2db13852018-01-08 22:28:57 -08001398 if OPTIONS.verbose:
1399 print("--- target info ---")
1400 common.DumpInfoDict(OPTIONS.info_dict)
1401
1402 # Load the source build dict if applicable.
1403 if OPTIONS.incremental_source is not None:
1404 OPTIONS.target_info_dict = OPTIONS.info_dict
1405 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1406 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1407
1408 if OPTIONS.verbose:
1409 print("--- source info ---")
1410 common.DumpInfoDict(OPTIONS.source_info_dict)
1411
1412 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001413 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1414
Tao Baoc098e9e2016-01-07 13:03:56 -08001415 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1416
Christian Oderf63e2cd2017-05-01 22:30:15 +02001417 # Use the default key to sign the package if not specified with package_key.
1418 # package_keys are needed on ab_updates, so always define them if an
1419 # ab_update is getting created.
1420 if not OPTIONS.no_signing or ab_update:
1421 if OPTIONS.package_key is None:
1422 OPTIONS.package_key = OPTIONS.info_dict.get(
1423 "default_system_dev_certificate",
1424 "build/target/product/security/testkey")
1425 # Get signing keys
1426 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1427
Tao Baoc098e9e2016-01-07 13:03:56 -08001428 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001429 WriteABOTAPackageWithBrilloScript(
1430 target_file=args[0],
1431 output_file=args[1],
1432 source_file=OPTIONS.incremental_source)
1433
Tao Bao89fbb0f2017-01-10 10:47:58 -08001434 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001435 return
1436
Tao Bao2db13852018-01-08 22:28:57 -08001437 # Sanity check the loaded info dicts first.
1438 if OPTIONS.info_dict.get("no_recovery") == "true":
1439 raise common.ExternalError(
1440 "--- target build has specified no recovery ---")
1441
1442 # Non-A/B OTAs rely on /cache partition to store temporary files.
1443 cache_size = OPTIONS.info_dict.get("cache_size")
1444 if cache_size is None:
1445 print("--- can't determine the cache partition size ---")
1446 OPTIONS.cache_size = cache_size
1447
Doug Zongker1c390a22009-05-14 19:06:36 -07001448 if OPTIONS.extra_script is not None:
1449 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1450
Dan Willemsencea5cd22017-03-21 14:44:27 -07001451 if OPTIONS.extracted_input is not None:
1452 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001453 input_zip = zipfile.ZipFile(args[0], "r")
1454 else:
1455 print("unzipping target target-files...")
1456 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1457 args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001458 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001459
Tao Bao2db13852018-01-08 22:28:57 -08001460 # If the caller explicitly specified the device-specific extensions path via
1461 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1462 # is present in the target target_files. Otherwise, take the path of the file
1463 # from 'tool_extensions' in the info dict and look for that in the local
1464 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001465 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001466 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1467 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001468 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001469 OPTIONS.device_specific = from_input
1470 else:
Tao Bao2db13852018-01-08 22:28:57 -08001471 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001472
Doug Zongker37974732010-09-16 17:44:38 -07001473 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001474 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001475
Tao Bao767e3ac2015-11-10 12:19:19 -08001476 # Set up the output zip. Create a temporary zip file if signing is needed.
1477 if OPTIONS.no_signing:
1478 if os.path.exists(args[1]):
1479 os.unlink(args[1])
1480 output_zip = zipfile.ZipFile(args[1], "w",
1481 compression=zipfile.ZIP_DEFLATED)
1482 else:
1483 temp_zip_file = tempfile.NamedTemporaryFile()
1484 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1485 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001486
Tao Bao767e3ac2015-11-10 12:19:19 -08001487 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001488 if OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001489 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001490
Tao Bao32b80dc2018-01-08 22:50:47 -08001491 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001492 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001493 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001494 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001495 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001496 UNZIP_PATTERN)
Tao Bao32b80dc2018-01-08 22:50:47 -08001497
1498 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
1499
1500 if OPTIONS.log_diff:
1501 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001502 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001503 target_files_diff.recursiveDiff(
1504 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001505
Tao Bao2db13852018-01-08 22:28:57 -08001506 common.ZipClose(input_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001507 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001508
Tao Bao767e3ac2015-11-10 12:19:19 -08001509 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001510 if not OPTIONS.no_signing:
1511 SignOutput(temp_zip_file.name, args[1])
1512 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001513
Tao Bao89fbb0f2017-01-10 10:47:58 -08001514 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001515
1516
1517if __name__ == '__main__':
1518 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001519 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001520 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001521 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001522 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001523 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001524 finally:
1525 common.Cleanup()