blob: 0044a8774bdb193712c8fbc1909095990bcf8b9d [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 Zongkereef39442009-04-02 12:14:19 -0700143
Tao Bao481bab82017-12-21 11:23:09 -0800144if sys.hexversion < 0x02070000:
145 print("Python 2.7 or newer is required.", file=sys.stderr)
146 sys.exit(1)
147
148
Doug Zongkereef39442009-04-02 12:14:19 -0700149OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700150OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700151OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700152OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700153OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700154OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800155OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800156OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700157OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700158OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
159if OPTIONS.worker_threads == 0:
160 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800161OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900162OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800163OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800164OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700165OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800166OPTIONS.oem_no_mount = False
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
Tao Bao481bab82017-12-21 11:23:09 -0800182class BuildInfo(object):
183 """A class that holds the information for a given build.
184
185 This class wraps up the property querying for a given source or target build.
186 It abstracts away the logic of handling OEM-specific properties, and caches
187 the commonly used properties such as fingerprint.
188
189 There are two types of info dicts: a) build-time info dict, which is generated
190 at build time (i.e. included in a target_files zip); b) OEM info dict that is
191 specified at package generation time (via command line argument
192 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
193 having "oem_fingerprint_properties" in build-time info dict), all the queries
194 would be answered based on build-time info dict only. Otherwise if using
195 OEM-specific properties, some of them will be calculated from two info dicts.
196
197 Users can query properties similarly as using a dict() (e.g. info['fstab']),
198 or to query build properties via GetBuildProp() or GetVendorBuildProp().
199
200 Attributes:
201 info_dict: The build-time info dict.
202 is_ab: Whether it's a build that uses A/B OTA.
203 oem_dicts: A list of OEM dicts.
204 oem_props: A list of OEM properties that should be read from OEM dicts; None
205 if the build doesn't use any OEM-specific property.
206 fingerprint: The fingerprint of the build, which would be calculated based
207 on OEM properties if applicable.
208 device: The device name, which could come from OEM dicts if applicable.
209 """
210
211 def __init__(self, info_dict, oem_dicts):
212 """Initializes a BuildInfo instance with the given dicts.
213
214 Arguments:
215 info_dict: The build-time info dict.
216 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
217 that it always uses the first dict to calculate the fingerprint or the
218 device name. The rest would be used for asserting OEM properties only
219 (e.g. one package can be installed on one of these devices).
220 """
221 self.info_dict = info_dict
222 self.oem_dicts = oem_dicts
223
224 self._is_ab = info_dict.get("ab_update") == "true"
225 self._oem_props = info_dict.get("oem_fingerprint_properties")
226
227 if self._oem_props:
228 assert oem_dicts, "OEM source required for this build"
229
230 # These two should be computed only after setting self._oem_props.
231 self._device = self.GetOemProperty("ro.product.device")
232 self._fingerprint = self.CalculateFingerprint()
233
234 @property
235 def is_ab(self):
236 return self._is_ab
237
238 @property
239 def device(self):
240 return self._device
241
242 @property
243 def fingerprint(self):
244 return self._fingerprint
245
246 @property
247 def oem_props(self):
248 return self._oem_props
249
250 def __getitem__(self, key):
251 return self.info_dict[key]
252
253 def get(self, key, default=None):
254 return self.info_dict.get(key, default)
255
256 def GetBuildProp(self, prop):
257 """Returns the inquired build property."""
258 try:
259 return self.info_dict.get("build.prop", {})[prop]
260 except KeyError:
261 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
262
263 def GetVendorBuildProp(self, prop):
264 """Returns the inquired vendor build property."""
265 try:
266 return self.info_dict.get("vendor.build.prop", {})[prop]
267 except KeyError:
268 raise common.ExternalError(
269 "couldn't find %s in vendor.build.prop" % (prop,))
270
271 def GetOemProperty(self, key):
272 if self.oem_props is not None and key in self.oem_props:
273 return self.oem_dicts[0][key]
274 return self.GetBuildProp(key)
275
276 def CalculateFingerprint(self):
277 if self.oem_props is None:
278 return self.GetBuildProp("ro.build.fingerprint")
279 return "%s/%s/%s:%s" % (
280 self.GetOemProperty("ro.product.brand"),
281 self.GetOemProperty("ro.product.name"),
282 self.GetOemProperty("ro.product.device"),
283 self.GetBuildProp("ro.build.thumbprint"))
284
285 def WriteMountOemScript(self, script):
286 assert self.oem_props is not None
287 recovery_mount_options = self.info_dict.get("recovery_mount_options")
288 script.Mount("/oem", recovery_mount_options)
289
290 def WriteDeviceAssertions(self, script, oem_no_mount):
291 # Read the property directly if not using OEM properties.
292 if not self.oem_props:
293 script.AssertDevice(self.device)
294 return
295
296 # Otherwise assert OEM properties.
297 if not self.oem_dicts:
298 raise common.ExternalError(
299 "No OEM file provided to answer expected assertions")
300
301 for prop in self.oem_props.split():
302 values = []
303 for oem_dict in self.oem_dicts:
304 if prop in oem_dict:
305 values.append(oem_dict[prop])
306 if not values:
307 raise common.ExternalError(
308 "The OEM file is missing the property %s" % (prop,))
309 script.AssertOemProperty(prop, values, oem_no_mount)
310
311
Tao Baofabe0832018-01-17 15:52:28 -0800312class PayloadSigner(object):
313 """A class that wraps the payload signing works.
314
315 When generating a Payload, hashes of the payload and metadata files will be
316 signed with the device key, either by calling an external payload signer or
317 by calling openssl with the package key. This class provides a unified
318 interface, so that callers can just call PayloadSigner.Sign().
319
320 If an external payload signer has been specified (OPTIONS.payload_signer), it
321 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
322 that the signing key should be provided as part of the payload_signer_args.
323 Otherwise without an external signer, it uses the package key
324 (OPTIONS.package_key) and calls openssl for the signing works.
325 """
326
327 def __init__(self):
328 if OPTIONS.payload_signer is None:
329 # Prepare the payload signing key.
330 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
331 pw = OPTIONS.key_passwords[OPTIONS.package_key]
332
333 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
334 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
335 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
336 cmd.extend(["-out", signing_key])
337
338 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
339 stderr=subprocess.STDOUT)
340 stdoutdata, _ = get_signing_key.communicate()
341 assert get_signing_key.returncode == 0, \
342 "Failed to get signing key: {}".format(stdoutdata)
343
344 self.signer = "openssl"
345 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
346 "-pkeyopt", "digest:sha256"]
347 else:
348 self.signer = OPTIONS.payload_signer
349 self.signer_args = OPTIONS.payload_signer_args
350
351 def Sign(self, in_file):
352 """Signs the given input file. Returns the output filename."""
353 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
354 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
355 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
356 stdoutdata, _ = signing.communicate()
357 assert signing.returncode == 0, \
358 "Failed to sign the input file: {}".format(stdoutdata)
359 return out_file
360
361
Doug Zongkereef39442009-04-02 12:14:19 -0700362def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200363 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700364
Doug Zongker951495f2009-08-14 12:44:19 -0700365 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
366 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700367
368
Tao Bao481bab82017-12-21 11:23:09 -0800369def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800370 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800371 if not oem_source:
372 return None
373
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800374 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800375 for oem_file in oem_source:
376 with open(oem_file) as fp:
377 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800378 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700379
Doug Zongkereef39442009-04-02 12:14:19 -0700380
Tao Baod42e97e2016-11-30 12:11:57 -0800381def _WriteRecoveryImageToBoot(script, output_zip):
382 """Find and write recovery image to /boot in two-step OTA.
383
384 In two-step OTAs, we write recovery image to /boot as the first step so that
385 we can reboot to there and install a new recovery image to /recovery.
386 A special "recovery-two-step.img" will be preferred, which encodes the correct
387 path of "/boot". Otherwise the device may show "device is corrupt" message
388 when booting into /boot.
389
390 Fall back to using the regular recovery.img if the two-step recovery image
391 doesn't exist. Note that rebuilding the special image at this point may be
392 infeasible, because we don't have the desired boot signer and keys when
393 calling ota_from_target_files.py.
394 """
395
396 recovery_two_step_img_name = "recovery-two-step.img"
397 recovery_two_step_img_path = os.path.join(
398 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
399 if os.path.exists(recovery_two_step_img_path):
400 recovery_two_step_img = common.GetBootableImage(
401 recovery_two_step_img_name, recovery_two_step_img_name,
402 OPTIONS.input_tmp, "RECOVERY")
403 common.ZipWriteStr(
404 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800405 print("two-step package: using %s in stage 1/3" % (
406 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800407 script.WriteRawImage("/boot", recovery_two_step_img_name)
408 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800409 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800410 # The "recovery.img" entry has been written into package earlier.
411 script.WriteRawImage("/boot", "recovery.img")
412
413
Doug Zongkerc9253822014-02-04 12:17:58 -0800414def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700415 namelist = [name for name in target_files_zip.namelist()]
416 return ("SYSTEM/recovery-from-boot.p" in namelist or
417 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700418
Tao Bao457cbf62017-03-06 09:56:01 -0800419
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700420def HasVendorPartition(target_files_zip):
421 try:
422 target_files_zip.getinfo("VENDOR/")
423 return True
424 except KeyError:
425 return False
426
Tao Bao457cbf62017-03-06 09:56:01 -0800427
Tao Bao481bab82017-12-21 11:23:09 -0800428def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700429 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800430 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700431
432
Tao Bao481bab82017-12-21 11:23:09 -0800433def WriteFingerprintAssertion(script, target_info, source_info):
434 source_oem_props = source_info.oem_props
435 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700436
Tao Bao481bab82017-12-21 11:23:09 -0800437 if source_oem_props is None and target_oem_props is None:
438 script.AssertSomeFingerprint(
439 source_info.fingerprint, target_info.fingerprint)
440 elif source_oem_props is not None and target_oem_props is not None:
441 script.AssertSomeThumbprint(
442 target_info.GetBuildProp("ro.build.thumbprint"),
443 source_info.GetBuildProp("ro.build.thumbprint"))
444 elif source_oem_props is None and target_oem_props is not None:
445 script.AssertFingerprintOrThumbprint(
446 source_info.fingerprint,
447 target_info.GetBuildProp("ro.build.thumbprint"))
448 else:
449 script.AssertFingerprintOrThumbprint(
450 target_info.fingerprint,
451 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700452
Doug Zongkerfc44a512014-08-26 13:10:25 -0700453
Tao Bao481bab82017-12-21 11:23:09 -0800454def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
455 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700456 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700457
458 Metadata used for on-device compatibility verification is retrieved from
459 target_zip then added to compatibility.zip which is added to the output_zip
460 archive.
461
Tao Baobcd1d162017-08-26 13:10:26 -0700462 Compatibility archive should only be included for devices that have enabled
463 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700464
465 Args:
466 target_zip: Zip file containing the source files to be included for OTA.
467 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800468 target_info: The BuildInfo instance that holds the target build info.
469 source_info: The BuildInfo instance that holds the source build info, if
470 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700471 """
472
Tao Baobcd1d162017-08-26 13:10:26 -0700473 def AddCompatibilityArchive(system_updated, vendor_updated):
474 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700475
Tao Baobcd1d162017-08-26 13:10:26 -0700476 Args:
477 system_updated: If True, the system image will be updated and therefore
478 its metadata should be included.
479 vendor_updated: If True, the vendor image will be updated and therefore
480 its metadata should be included.
481 """
482 # Determine what metadata we need. Files are names relative to META/.
483 compatibility_files = []
484 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
485 system_metadata = ("system_manifest.xml", "system_matrix.xml")
486 if vendor_updated:
487 compatibility_files += vendor_metadata
488 if system_updated:
489 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700490
Tao Baobcd1d162017-08-26 13:10:26 -0700491 # Create new archive.
492 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800493 compatibility_archive_zip = zipfile.ZipFile(
494 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700495
Tao Baobcd1d162017-08-26 13:10:26 -0700496 # Add metadata.
497 for file_name in compatibility_files:
498 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700499
Tao Baobcd1d162017-08-26 13:10:26 -0700500 if target_file_name in target_zip.namelist():
501 data = target_zip.read(target_file_name)
502 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700503
Tao Baobcd1d162017-08-26 13:10:26 -0700504 # Ensure files are written before we copy into output_zip.
505 compatibility_archive_zip.close()
506
507 # Only add the archive if we have any compatibility info.
508 if compatibility_archive_zip.namelist():
509 common.ZipWrite(output_zip, compatibility_archive.name,
510 arcname="compatibility.zip",
511 compress_type=zipfile.ZIP_STORED)
512
513 # Will only proceed if the target has enabled the Treble support (as well as
514 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800515 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700516 return
517
518 # We don't support OEM thumbprint in Treble world (which calculates
519 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800520 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700521
522 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800523 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700524 AddCompatibilityArchive(True, True)
525 return
526
Tao Bao481bab82017-12-21 11:23:09 -0800527 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700528
Tao Bao481bab82017-12-21 11:23:09 -0800529 source_fp = source_info.fingerprint
530 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700531 system_updated = source_fp != target_fp
532
Tao Bao481bab82017-12-21 11:23:09 -0800533 source_fp_vendor = source_info.GetVendorBuildProp(
534 "ro.vendor.build.fingerprint")
535 target_fp_vendor = target_info.GetVendorBuildProp(
536 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700537 vendor_updated = source_fp_vendor != target_fp_vendor
538
539 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700540
541
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700542def WriteFullOTAPackage(input_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800543 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700544
Tao Bao481bab82017-12-21 11:23:09 -0800545 # We don't know what version it will be installed on top of. We expect the API
546 # just won't change very often. Similarly for fstab, it might have changed in
547 # the target build.
548 target_api_version = target_info["recovery_api_version"]
549 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700550
Tao Bao481bab82017-12-21 11:23:09 -0800551 if target_info.oem_props and not OPTIONS.oem_no_mount:
552 target_info.WriteMountOemScript(script)
553
Tao Baodf3a48b2018-01-10 16:30:43 -0800554 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700555
Doug Zongker05d3dea2009-06-22 11:32:31 -0700556 device_specific = common.DeviceSpecificParams(
557 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800558 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700559 output_zip=output_zip,
560 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700561 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700562 metadata=metadata,
563 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700564
Tao Bao457cbf62017-03-06 09:56:01 -0800565 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800566
Tao Bao481bab82017-12-21 11:23:09 -0800567 # Assertions (e.g. downgrade check, device properties check).
568 ts = target_info.GetBuildProp("ro.build.date.utc")
569 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700570 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700571
Tao Bao481bab82017-12-21 11:23:09 -0800572 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700573 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800574
575 # Two-step package strategy (in chronological order, which is *not*
576 # the order in which the generated script has things):
577 #
578 # if stage is not "2/3" or "3/3":
579 # write recovery image to boot partition
580 # set stage to "2/3"
581 # reboot to boot partition and restart recovery
582 # else if stage is "2/3":
583 # write recovery image to recovery partition
584 # set stage to "3/3"
585 # reboot to recovery partition and restart recovery
586 # else:
587 # (stage must be "3/3")
588 # set stage to ""
589 # do normal full package installation:
590 # wipe and install system, boot image, etc.
591 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700592 # complete script normally
593 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800594
595 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
596 OPTIONS.input_tmp, "RECOVERY")
597 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800598 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800599 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800600 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800601 assert fs.fs_type.upper() == "EMMC", \
602 "two-step packages only supported on devices with EMMC /misc partitions"
603 bcb_dev = {"bcb_dev": fs.device}
604 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
605 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700606if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800607""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800608
609 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
610 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800611 script.WriteRawImage("/recovery", "recovery.img")
612 script.AppendExtra("""
613set_stage("%(bcb_dev)s", "3/3");
614reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700615else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800616""" % bcb_dev)
617
Tao Baod42e97e2016-11-30 12:11:57 -0800618 # Stage 3/3: Make changes.
619 script.Comment("Stage 3/3")
620
Tao Bao6c55a8a2015-04-08 15:30:27 -0700621 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800622 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700623
Doug Zongkere5ff5902012-01-17 10:55:37 -0800624 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700625
Doug Zongker01ce19c2014-02-04 13:48:15 -0800626 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700627
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700628 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800629 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700630 if HasVendorPartition(input_zip):
631 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700632
Doug Zongker4b9596f2014-06-09 14:15:45 -0700633 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800634
Tao Bao457cbf62017-03-06 09:56:01 -0800635 # Full OTA is done as an "incremental" against an empty source image. This
636 # has the effect of writing new data from the package to the entire
637 # partition, but lets us reuse the updater code that writes incrementals to
638 # do it.
Tao Baoc765cca2018-01-31 17:32:40 -0800639 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip)
Tao Bao457cbf62017-03-06 09:56:01 -0800640 system_tgt.ResetFileMap()
641 system_diff = common.BlockDifference("system", system_tgt, src=None)
642 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700643
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700644 boot_img = common.GetBootableImage(
645 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800646
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700647 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700648 script.ShowProgress(0.1, 0)
649
Tao Baoc765cca2018-01-31 17:32:40 -0800650 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip)
Tao Bao457cbf62017-03-06 09:56:01 -0800651 vendor_tgt.ResetFileMap()
652 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
653 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700654
Tao Bao481bab82017-12-21 11:23:09 -0800655 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700656
Tao Bao481bab82017-12-21 11:23:09 -0800657 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700658 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700659
Doug Zongker01ce19c2014-02-04 13:48:15 -0800660 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700661 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700662
Doug Zongker01ce19c2014-02-04 13:48:15 -0800663 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700664 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700665
Doug Zongker1c390a22009-05-14 19:06:36 -0700666 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700667 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700668
Doug Zongker14833602010-02-02 13:12:04 -0800669 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800670
Doug Zongker922206e2014-03-04 13:16:24 -0800671 if OPTIONS.wipe_user_data:
672 script.ShowProgress(0.1, 10)
673 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700674
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800675 if OPTIONS.two_step:
676 script.AppendExtra("""
677set_stage("%(bcb_dev)s", "");
678""" % bcb_dev)
679 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800680
681 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
682 script.Comment("Stage 1/3")
683 _WriteRecoveryImageToBoot(script, output_zip)
684
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800685 script.AppendExtra("""
686set_stage("%(bcb_dev)s", "2/3");
687reboot_now("%(bcb_dev)s", "");
688endif;
689endif;
690""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800691
Tao Bao5d182562016-02-23 11:38:39 -0800692 script.SetProgress(1)
693 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800694 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700695 WriteMetadata(metadata, output_zip)
696
Doug Zongkerfc44a512014-08-26 13:10:25 -0700697
Doug Zongker2ea21062010-04-28 16:05:21 -0700698def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800699 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
700 common.ZipWriteStr(output_zip, METADATA_NAME, value,
701 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700702
Doug Zongkerfc44a512014-08-26 13:10:25 -0700703
Tao Bao481bab82017-12-21 11:23:09 -0800704def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800705 # Only incremental OTAs are allowed to reach here.
706 assert OPTIONS.incremental_source is not None
707
Tao Bao481bab82017-12-21 11:23:09 -0800708 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
709 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800710 is_downgrade = long(post_timestamp) < long(pre_timestamp)
711
712 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800713 if not is_downgrade:
714 raise RuntimeError("--downgrade specified but no downgrade detected: "
715 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800716 metadata["ota-downgrade"] = "yes"
717 elif OPTIONS.timestamp:
718 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800719 raise RuntimeError("--override_timestamp specified but no timestamp hack "
720 "needed: pre: %s, post: %s" % (pre_timestamp,
721 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800722 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800723 else:
724 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800725 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800726 "pre: %s, post: %s. Need to specify "
727 "--override_timestamp OR --downgrade to allow "
728 "building the incremental." % (pre_timestamp,
729 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800730 metadata["post-timestamp"] = post_timestamp
731
732
Tao Baodf3a48b2018-01-10 16:30:43 -0800733def GetPackageMetadata(target_info, source_info=None):
734 """Generates and returns the metadata dict.
735
736 It generates a dict() that contains the info to be written into an OTA
737 package (META-INF/com/android/metadata). It also handles the detection of
738 downgrade / timestamp override / data wipe based on the global options.
739
740 Args:
741 target_info: The BuildInfo instance that holds the target build info.
742 source_info: The BuildInfo instance that holds the source build info, or
743 None if generating full OTA.
744
745 Returns:
746 A dict to be written into package metadata entry.
747 """
748 assert isinstance(target_info, BuildInfo)
749 assert source_info is None or isinstance(source_info, BuildInfo)
750
751 metadata = {
752 'post-build' : target_info.fingerprint,
753 'post-build-incremental' : target_info.GetBuildProp(
754 'ro.build.version.incremental'),
755 }
756
757 if target_info.is_ab:
758 metadata['ota-type'] = 'AB'
759 metadata['ota-required-cache'] = '0'
760 else:
761 metadata['ota-type'] = 'BLOCK'
762
763 if OPTIONS.wipe_user_data:
764 metadata['ota-wipe'] = 'yes'
765
766 is_incremental = source_info is not None
767 if is_incremental:
768 metadata['pre-build'] = source_info.fingerprint
769 metadata['pre-build-incremental'] = source_info.GetBuildProp(
770 'ro.build.version.incremental')
771 metadata['pre-device'] = source_info.device
772 else:
773 metadata['pre-device'] = target_info.device
774
775 # Detect downgrades, or fill in the post-timestamp.
776 if is_incremental:
777 HandleDowngradeMetadata(metadata, target_info, source_info)
778 else:
779 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
780
781 return metadata
782
783
Geremy Condra36bd3652014-02-06 19:45:10 -0800784def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800785 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
786 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800787
Tao Bao481bab82017-12-21 11:23:09 -0800788 target_api_version = target_info["recovery_api_version"]
789 source_api_version = source_info["recovery_api_version"]
790 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700791 print("WARNING: generating edify script for a source that "
792 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800793
Tao Bao481bab82017-12-21 11:23:09 -0800794 script = edify_generator.EdifyGenerator(
795 source_api_version, target_info, fstab=source_info["fstab"])
796
797 if target_info.oem_props or source_info.oem_props:
798 if not OPTIONS.oem_no_mount:
799 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -0700800
Tao Baodf3a48b2018-01-10 16:30:43 -0800801 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -0800802
Geremy Condra36bd3652014-02-06 19:45:10 -0800803 device_specific = common.DeviceSpecificParams(
804 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800805 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800806 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800807 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800808 output_zip=output_zip,
809 script=script,
810 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -0800811 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800812
Geremy Condra36bd3652014-02-06 19:45:10 -0800813 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800814 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800815 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800816 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800817 updating_boot = (not OPTIONS.two_step and
818 (source_boot.data != target_boot.data))
819
Geremy Condra36bd3652014-02-06 19:45:10 -0800820 target_recovery = common.GetBootableImage(
821 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800822
Tao Baoc765cca2018-01-31 17:32:40 -0800823 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip)
824 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip)
Tao Baodd2a5892015-03-12 12:32:37 -0700825
Tao Bao0582cb62017-12-21 11:47:01 -0800826 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -0800827 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -0800828 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -0700829
Tao Baof8acad12016-07-07 09:09:58 -0700830 # Check the first block of the source system partition for remount R/W only
831 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -0800832 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700833 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700834 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
835 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
836 # b) the blocks listed in block map may not contain all the bytes for a given
837 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -0800838 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700839 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
840 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700841 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800842 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700843 version=blockimgdiff_version,
844 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700845
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700846 if HasVendorPartition(target_zip):
847 if not HasVendorPartition(source_zip):
848 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoc765cca2018-01-31 17:32:40 -0800849 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip)
850 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800851
852 # Check first block of vendor partition for remount R/W only if
853 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -0800854 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800855 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700856 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700857 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800858 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700859 version=blockimgdiff_version,
860 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700861 else:
862 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800863
Tao Baobcd1d162017-08-26 13:10:26 -0700864 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -0800865 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700866
Tao Bao481bab82017-12-21 11:23:09 -0800867 # Assertions (e.g. device properties check).
868 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -0800869 device_specific.IncrementalOTA_Assertions()
870
871 # Two-step incremental package strategy (in chronological order,
872 # which is *not* the order in which the generated script has
873 # things):
874 #
875 # if stage is not "2/3" or "3/3":
876 # do verification on current system
877 # write recovery image to boot partition
878 # set stage to "2/3"
879 # reboot to boot partition and restart recovery
880 # else if stage is "2/3":
881 # write recovery image to recovery partition
882 # set stage to "3/3"
883 # reboot to recovery partition and restart recovery
884 # else:
885 # (stage must be "3/3")
886 # perform update:
887 # patch system files, etc.
888 # force full install of new boot image
889 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700890 # complete script normally
891 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800892
893 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800894 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -0800895 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700896 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800897 assert fs.fs_type.upper() == "EMMC", \
898 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -0800899 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -0800900 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
901 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700902if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800903""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800904
905 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
906 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -0700907 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800908 script.WriteRawImage("/recovery", "recovery.img")
909 script.AppendExtra("""
910set_stage("%(bcb_dev)s", "3/3");
911reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700912else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800913""" % bcb_dev)
914
Tao Baod42e97e2016-11-30 12:11:57 -0800915 # Stage 1/3: (a) Verify the current system.
916 script.Comment("Stage 1/3")
917
Tao Bao6c55a8a2015-04-08 15:30:27 -0700918 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800919 script.Print("Source: {}".format(source_info.fingerprint))
920 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700921
Geremy Condra36bd3652014-02-06 19:45:10 -0800922 script.Print("Verifying current system...")
923
924 device_specific.IncrementalOTA_VerifyBegin()
925
Tao Bao481bab82017-12-21 11:23:09 -0800926 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800927
Tao Baod8d14be2016-02-04 14:26:02 -0800928 # Check the required cache size (i.e. stashed blocks).
929 size = []
930 if system_diff:
931 size.append(system_diff.required_cache)
932 if vendor_diff:
933 size.append(vendor_diff.required_cache)
934
Geremy Condra36bd3652014-02-06 19:45:10 -0800935 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -0800936 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800937 d = common.Difference(target_boot, source_boot)
938 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700939 if d is None:
940 include_full_boot = True
941 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
942 else:
943 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800944
Tao Bao89fbb0f2017-01-10 10:47:58 -0800945 print("boot target: %d source: %d diff: %d" % (
946 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -0800947
Doug Zongkerf8340082014-08-05 10:39:37 -0700948 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800949
Doug Zongkerf8340082014-08-05 10:39:37 -0700950 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
951 (boot_type, boot_device,
952 source_boot.size, source_boot.sha1,
953 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800954 size.append(target_boot.size)
955
956 if size:
957 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800958
959 device_specific.IncrementalOTA_VerifyEnd()
960
961 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -0800962 # Stage 1/3: (b) Write recovery image to /boot.
963 _WriteRecoveryImageToBoot(script, output_zip)
964
Geremy Condra36bd3652014-02-06 19:45:10 -0800965 script.AppendExtra("""
966set_stage("%(bcb_dev)s", "2/3");
967reboot_now("%(bcb_dev)s", "");
968else
969""" % bcb_dev)
970
Tao Baod42e97e2016-11-30 12:11:57 -0800971 # Stage 3/3: Make changes.
972 script.Comment("Stage 3/3")
973
Jesse Zhao75bcea02015-01-06 10:59:53 -0800974 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -0700975 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800976 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -0700977 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800978
Geremy Condra36bd3652014-02-06 19:45:10 -0800979 script.Comment("---- start making changes here ----")
980
981 device_specific.IncrementalOTA_InstallBegin()
982
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700983 system_diff.WriteScript(script, output_zip,
984 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700985
Doug Zongkerfc44a512014-08-26 13:10:25 -0700986 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700987 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800988
989 if OPTIONS.two_step:
990 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
991 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -0800992 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -0800993
994 if not OPTIONS.two_step:
995 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700996 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800997 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -0700998 script.Print("Installing boot image...")
999 script.WriteRawImage("/boot", "boot.img")
1000 else:
1001 # Produce the boot image by applying a patch to the current
1002 # contents of the boot partition, and write it back to the
1003 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001004 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001005 script.Print("Patching boot image...")
1006 script.ShowProgress(0.1, 10)
1007 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1008 % (boot_type, boot_device,
1009 source_boot.size, source_boot.sha1,
1010 target_boot.size, target_boot.sha1),
1011 "-",
1012 target_boot.size, target_boot.sha1,
1013 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001014 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001015 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001016
1017 # Do device-specific installation (eg, write radio image).
1018 device_specific.IncrementalOTA_InstallEnd()
1019
1020 if OPTIONS.extra_script is not None:
1021 script.AppendExtra(OPTIONS.extra_script)
1022
Doug Zongker922206e2014-03-04 13:16:24 -08001023 if OPTIONS.wipe_user_data:
1024 script.Print("Erasing user data...")
1025 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001026 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001027
Geremy Condra36bd3652014-02-06 19:45:10 -08001028 if OPTIONS.two_step:
1029 script.AppendExtra("""
1030set_stage("%(bcb_dev)s", "");
1031endif;
1032endif;
1033""" % bcb_dev)
1034
1035 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001036 # For downgrade OTAs, we prefer to use the update-binary in the source
1037 # build that is actually newer than the one in the target build.
1038 if OPTIONS.downgrade:
1039 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1040 else:
1041 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001042 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001043 WriteMetadata(metadata, output_zip)
1044
Doug Zongker32b527d2014-03-04 10:03:02 -08001045
Tao Baoc098e9e2016-01-07 13:03:56 -08001046def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1047 source_file=None):
1048 """Generate an Android OTA package that has A/B update payload."""
1049
Tao Bao2dd1c482017-02-03 16:49:39 -08001050 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1051 expected_length=None):
1052 """Compute the streaming metadata for a given zip.
1053
1054 When 'reserve_space' is True, we reserve extra space for the offset and
1055 length of the metadata entry itself, although we don't know the final
1056 values until the package gets signed. This function will be called again
1057 after signing. We then write the actual values and pad the string to the
1058 length we set earlier. Note that we can't use the actual length of the
1059 metadata entry in the second run. Otherwise the offsets for other entries
1060 will be changing again.
1061 """
Tao Baoc96316c2017-01-24 22:10:49 -08001062
1063 def ComputeEntryOffsetSize(name):
1064 """Compute the zip entry offset and size."""
1065 info = zip_file.getinfo(name)
1066 offset = info.header_offset + len(info.FileHeader())
1067 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001068 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001069
1070 # payload.bin and payload_properties.txt must exist.
1071 offsets = [ComputeEntryOffsetSize('payload.bin'),
1072 ComputeEntryOffsetSize('payload_properties.txt')]
1073
1074 # care_map.txt is available only if dm-verity is enabled.
1075 if 'care_map.txt' in zip_file.namelist():
1076 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001077
Tao Bao21803d32017-04-19 10:16:09 -07001078 if 'compatibility.zip' in zip_file.namelist():
1079 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1080
Tao Bao2dd1c482017-02-03 16:49:39 -08001081 # 'META-INF/com/android/metadata' is required. We don't know its actual
1082 # offset and length (as well as the values for other entries). So we
1083 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1084 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1085 # beginning of the zip), as well as the possible value changes in other
1086 # entries.
1087 if reserve_space:
1088 offsets.append('metadata:' + ' ' * 10)
1089 else:
1090 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1091
1092 value = ','.join(offsets)
1093 if expected_length is not None:
1094 assert len(value) <= expected_length, \
1095 'Insufficient reserved space: reserved=%d, actual=%d' % (
1096 expected_length, len(value))
1097 value += ' ' * (expected_length - len(value))
1098 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001099
Alex Deymod8d96ec2016-06-10 16:38:31 -07001100 # The place where the output from the subprocess should go.
1101 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1102
Tao Baofabe0832018-01-17 15:52:28 -08001103 # Get the PayloadSigner to be used in step 3.
1104 payload_signer = PayloadSigner()
Tao Baoc098e9e2016-01-07 13:03:56 -08001105
Tao Baodea0f8b2016-06-20 17:55:06 -07001106 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001107 temp_zip_file = tempfile.NamedTemporaryFile()
1108 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1109 compression=zipfile.ZIP_DEFLATED)
1110
Tao Bao481bab82017-12-21 11:23:09 -08001111 if source_file is not None:
1112 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1113 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1114 else:
1115 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1116 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001117
Tao Bao481bab82017-12-21 11:23:09 -08001118 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001119 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001120
Tao Baoc098e9e2016-01-07 13:03:56 -08001121 # 1. Generate payload.
1122 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1123 cmd = ["brillo_update_payload", "generate",
1124 "--payload", payload_file,
1125 "--target_image", target_file]
1126 if source_file is not None:
1127 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001128 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1129 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001130 assert p1.returncode == 0, "brillo_update_payload generate failed"
1131
1132 # 2. Generate hashes of the payload and metadata files.
1133 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1134 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1135 cmd = ["brillo_update_payload", "hash",
1136 "--unsigned_payload", payload_file,
1137 "--signature_size", "256",
1138 "--metadata_hash_file", metadata_sig_file,
1139 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001140 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1141 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001142 assert p1.returncode == 0, "brillo_update_payload hash failed"
1143
1144 # 3. Sign the hashes and insert them back into the payload file.
Tao Baoc098e9e2016-01-07 13:03:56 -08001145 # 3a. Sign the payload hash.
Tao Baofabe0832018-01-17 15:52:28 -08001146 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001147
1148 # 3b. Sign the metadata hash.
Tao Baofabe0832018-01-17 15:52:28 -08001149 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001150
1151 # 3c. Insert the signatures back into the payload file.
1152 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1153 suffix=".bin")
1154 cmd = ["brillo_update_payload", "sign",
1155 "--unsigned_payload", payload_file,
1156 "--payload", signed_payload_file,
1157 "--signature_size", "256",
1158 "--metadata_signature_file", signed_metadata_sig_file,
1159 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001160 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1161 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001162 assert p1.returncode == 0, "brillo_update_payload sign failed"
1163
Alex Deymo19241c12016-02-04 22:29:29 -08001164 # 4. Dump the signed payload properties.
1165 properties_file = common.MakeTempFile(prefix="payload-properties-",
1166 suffix=".txt")
1167 cmd = ["brillo_update_payload", "properties",
1168 "--payload", signed_payload_file,
1169 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001170 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1171 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001172 assert p1.returncode == 0, "brillo_update_payload properties failed"
1173
Tao Bao7c5dc572016-06-14 17:48:11 -07001174 if OPTIONS.wipe_user_data:
1175 with open(properties_file, "a") as f:
1176 f.write("POWERWASH=1\n")
1177 metadata["ota-wipe"] = "yes"
1178
Tao Baoc96316c2017-01-24 22:10:49 -08001179 # Add the signed payload file and properties into the zip. In order to
1180 # support streaming, we pack payload.bin, payload_properties.txt and
1181 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1182 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001183 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1184 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001185 common.ZipWrite(output_zip, properties_file,
1186 arcname="payload_properties.txt",
1187 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001188
Tianjie Xucfa86222016-03-07 16:31:19 -08001189 # If dm-verity is supported for the device, copy contents of care_map
1190 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001191 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001192 if (target_info.get("verity") == "true" or
1193 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001194 care_map_path = "META/care_map.txt"
1195 namelist = target_zip.namelist()
1196 if care_map_path in namelist:
1197 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001198 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001199 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001200 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001201 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001202
Tao Bao481bab82017-12-21 11:23:09 -08001203 # source_info must be None for full OTAs.
Tao Baobcd1d162017-08-26 13:10:26 -07001204 if source_file is None:
Tao Bao481bab82017-12-21 11:23:09 -08001205 assert source_info is None
Tao Bao21803d32017-04-19 10:16:09 -07001206
Tao Baobcd1d162017-08-26 13:10:26 -07001207 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001208 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001209
Tao Bao21803d32017-04-19 10:16:09 -07001210 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001211
Tao Bao2dd1c482017-02-03 16:49:39 -08001212 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001213 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001214 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001215 WriteMetadata(metadata, output_zip)
1216 common.ZipClose(output_zip)
1217
Tao Bao2dd1c482017-02-03 16:49:39 -08001218 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
Tao Bao89d7ab22017-12-14 17:05:33 -08001219 # ZIP entries, as well as padding the entry headers. We do a preliminary
Tao Bao2dd1c482017-02-03 16:49:39 -08001220 # signing (with an incomplete metadata entry) to allow that to happen. Then
Tao Bao89d7ab22017-12-14 17:05:33 -08001221 # compute the ZIP entry offsets, write back the final metadata and do the
Tao Bao2dd1c482017-02-03 16:49:39 -08001222 # final signing.
Tao Bao89d7ab22017-12-14 17:05:33 -08001223 prelim_signing = common.MakeTempFile(suffix='.zip')
1224 SignOutput(temp_zip_file.name, prelim_signing)
Tao Bao2dd1c482017-02-03 16:49:39 -08001225 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001226
Tao Bao2dd1c482017-02-03 16:49:39 -08001227 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Bao89d7ab22017-12-14 17:05:33 -08001228 prelim_signing_zip = zipfile.ZipFile(prelim_signing, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001229 expected_length = len(metadata['ota-streaming-property-files'])
1230 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao89d7ab22017-12-14 17:05:33 -08001231 prelim_signing_zip, reserve_space=False, expected_length=expected_length)
1232 common.ZipClose(prelim_signing_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001233
Tao Bao89d7ab22017-12-14 17:05:33 -08001234 # Replace the METADATA entry.
1235 common.ZipDelete(prelim_signing, METADATA_NAME)
1236 output_zip = zipfile.ZipFile(prelim_signing, 'a',
Tao Bao2dd1c482017-02-03 16:49:39 -08001237 compression=zipfile.ZIP_DEFLATED)
Tao Bao2dd1c482017-02-03 16:49:39 -08001238 WriteMetadata(metadata, output_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001239 common.ZipClose(output_zip)
1240
1241 # Re-sign the package after updating the metadata entry.
Tao Bao89d7ab22017-12-14 17:05:33 -08001242 SignOutput(prelim_signing, output_file)
Tao Bao2dd1c482017-02-03 16:49:39 -08001243
1244 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao89d7ab22017-12-14 17:05:33 -08001245 output_zip = zipfile.ZipFile(output_file, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001246 actual = metadata['ota-streaming-property-files'].strip()
1247 expected = ComputeStreamingMetadata(output_zip)
1248 assert actual == expected, \
1249 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001250 common.ZipClose(output_zip)
1251
Tao Baoc098e9e2016-01-07 13:03:56 -08001252
Doug Zongkereef39442009-04-02 12:14:19 -07001253def main(argv):
1254
1255 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001256 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001257 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001258 elif o in ("-i", "--incremental_from"):
1259 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001260 elif o == "--full_radio":
1261 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001262 elif o == "--full_bootloader":
1263 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001264 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001265 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001266 elif o == "--downgrade":
1267 OPTIONS.downgrade = True
1268 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001269 elif o == "--override_timestamp":
1270 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001271 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001272 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001273 elif o == "--oem_no_mount":
1274 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001275 elif o in ("-e", "--extra_script"):
1276 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001277 elif o in ("-t", "--worker_threads"):
1278 if a.isdigit():
1279 OPTIONS.worker_threads = int(a)
1280 else:
1281 raise ValueError("Cannot parse value %r for option %r - only "
1282 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001283 elif o in ("-2", "--two_step"):
1284 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001285 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001286 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001287 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001288 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001289 elif o == "--block":
1290 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001291 elif o in ("-b", "--binary"):
1292 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001293 elif o == "--stash_threshold":
1294 try:
1295 OPTIONS.stash_threshold = float(a)
1296 except ValueError:
1297 raise ValueError("Cannot parse value %r for option %r - expecting "
1298 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001299 elif o == "--log_diff":
1300 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001301 elif o == "--payload_signer":
1302 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001303 elif o == "--payload_signer_args":
1304 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001305 elif o == "--extracted_input_target_files":
1306 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001307 else:
1308 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001309 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001310
1311 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001312 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001313 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001314 "package_key=",
1315 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001316 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001317 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001318 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001319 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001320 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001321 "extra_script=",
1322 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001323 "two_step",
1324 "no_signing",
1325 "block",
1326 "binary=",
1327 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001328 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001329 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001330 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001331 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001332 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001333 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001334 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001335 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001336
1337 if len(args) != 2:
1338 common.Usage(__doc__)
1339 sys.exit(1)
1340
Tao Bao5d182562016-02-23 11:38:39 -08001341 if OPTIONS.downgrade:
1342 # Sanity check to enforce a data wipe.
1343 if not OPTIONS.wipe_user_data:
1344 raise ValueError("Cannot downgrade without a data wipe")
1345
1346 # We should only allow downgrading incrementals (as opposed to full).
1347 # Otherwise the device may go back from arbitrary build with this full
1348 # OTA package.
1349 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001350 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001351
Tao Bao3e6161a2017-02-28 11:48:48 -08001352 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1353 "Cannot have --downgrade AND --override_timestamp both"
1354
Tao Bao2db13852018-01-08 22:28:57 -08001355 # Load the build info dicts from the zip directly or the extracted input
1356 # directory. We don't need to unzip the entire target-files zips, because they
1357 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1358 # When loading the info dicts, we don't need to provide the second parameter
1359 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1360 # some properties with their actual paths, such as 'selinux_fc',
1361 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001362 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001363 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001364 else:
Tao Bao2db13852018-01-08 22:28:57 -08001365 with zipfile.ZipFile(args[0], 'r') as input_zip:
1366 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001367
Tao Bao2db13852018-01-08 22:28:57 -08001368 if OPTIONS.verbose:
1369 print("--- target info ---")
1370 common.DumpInfoDict(OPTIONS.info_dict)
1371
1372 # Load the source build dict if applicable.
1373 if OPTIONS.incremental_source is not None:
1374 OPTIONS.target_info_dict = OPTIONS.info_dict
1375 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1376 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1377
1378 if OPTIONS.verbose:
1379 print("--- source info ---")
1380 common.DumpInfoDict(OPTIONS.source_info_dict)
1381
1382 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001383 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1384
Tao Baoc098e9e2016-01-07 13:03:56 -08001385 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1386
Christian Oderf63e2cd2017-05-01 22:30:15 +02001387 # Use the default key to sign the package if not specified with package_key.
1388 # package_keys are needed on ab_updates, so always define them if an
1389 # ab_update is getting created.
1390 if not OPTIONS.no_signing or ab_update:
1391 if OPTIONS.package_key is None:
1392 OPTIONS.package_key = OPTIONS.info_dict.get(
1393 "default_system_dev_certificate",
1394 "build/target/product/security/testkey")
1395 # Get signing keys
1396 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1397
Tao Baoc098e9e2016-01-07 13:03:56 -08001398 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001399 WriteABOTAPackageWithBrilloScript(
1400 target_file=args[0],
1401 output_file=args[1],
1402 source_file=OPTIONS.incremental_source)
1403
Tao Bao89fbb0f2017-01-10 10:47:58 -08001404 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001405 return
1406
Tao Bao2db13852018-01-08 22:28:57 -08001407 # Sanity check the loaded info dicts first.
1408 if OPTIONS.info_dict.get("no_recovery") == "true":
1409 raise common.ExternalError(
1410 "--- target build has specified no recovery ---")
1411
1412 # Non-A/B OTAs rely on /cache partition to store temporary files.
1413 cache_size = OPTIONS.info_dict.get("cache_size")
1414 if cache_size is None:
1415 print("--- can't determine the cache partition size ---")
1416 OPTIONS.cache_size = cache_size
1417
Doug Zongker1c390a22009-05-14 19:06:36 -07001418 if OPTIONS.extra_script is not None:
1419 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1420
Dan Willemsencea5cd22017-03-21 14:44:27 -07001421 if OPTIONS.extracted_input is not None:
1422 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001423 input_zip = zipfile.ZipFile(args[0], "r")
1424 else:
1425 print("unzipping target target-files...")
1426 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1427 args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001428 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001429
Tao Bao2db13852018-01-08 22:28:57 -08001430 # If the caller explicitly specified the device-specific extensions path via
1431 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1432 # is present in the target target_files. Otherwise, take the path of the file
1433 # from 'tool_extensions' in the info dict and look for that in the local
1434 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001435 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001436 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1437 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001438 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001439 OPTIONS.device_specific = from_input
1440 else:
Tao Bao2db13852018-01-08 22:28:57 -08001441 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001442
Doug Zongker37974732010-09-16 17:44:38 -07001443 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001444 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001445
Tao Bao767e3ac2015-11-10 12:19:19 -08001446 # Set up the output zip. Create a temporary zip file if signing is needed.
1447 if OPTIONS.no_signing:
1448 if os.path.exists(args[1]):
1449 os.unlink(args[1])
1450 output_zip = zipfile.ZipFile(args[1], "w",
1451 compression=zipfile.ZIP_DEFLATED)
1452 else:
1453 temp_zip_file = tempfile.NamedTemporaryFile()
1454 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1455 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001456
Tao Bao767e3ac2015-11-10 12:19:19 -08001457 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001458 if OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001459 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001460
Tao Bao32b80dc2018-01-08 22:50:47 -08001461 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001462 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001463 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001464 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001465 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001466 UNZIP_PATTERN)
Tao Bao32b80dc2018-01-08 22:50:47 -08001467
1468 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
1469
1470 if OPTIONS.log_diff:
1471 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001472 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001473 target_files_diff.recursiveDiff(
1474 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001475
Tao Bao2db13852018-01-08 22:28:57 -08001476 common.ZipClose(input_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001477 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001478
Tao Bao767e3ac2015-11-10 12:19:19 -08001479 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001480 if not OPTIONS.no_signing:
1481 SignOutput(temp_zip_file.name, args[1])
1482 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001483
Tao Bao89fbb0f2017-01-10 10:47:58 -08001484 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001485
1486
1487if __name__ == '__main__':
1488 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001489 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001490 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001491 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001492 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001493 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001494 finally:
1495 common.Cleanup()