blob: 385acb4701a3c969e9ae4c52edd19d1ddf88eb25 [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
Tao Baoc7b403a2018-01-30 18:19:04 -0800363class Payload(object):
364 """Manages the creation and the signing of an A/B OTA Payload."""
365
366 PAYLOAD_BIN = 'payload.bin'
367 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
368
369 def __init__(self):
370 # The place where the output from the subprocess should go.
371 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
372 self.payload_file = None
373 self.payload_properties = None
374
375 def Generate(self, target_file, source_file=None, additional_args=None):
376 """Generates a payload from the given target-files zip(s).
377
378 Args:
379 target_file: The filename of the target build target-files zip.
380 source_file: The filename of the source build target-files zip; or None if
381 generating a full OTA.
382 additional_args: A list of additional args that should be passed to
383 brillo_update_payload script; or None.
384 """
385 if additional_args is None:
386 additional_args = []
387
388 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
389 cmd = ["brillo_update_payload", "generate",
390 "--payload", payload_file,
391 "--target_image", target_file]
392 if source_file is not None:
393 cmd.extend(["--source_image", source_file])
394 cmd.extend(additional_args)
395 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
396 stdoutdata, _ = p.communicate()
397 assert p.returncode == 0, \
398 "brillo_update_payload generate failed: {}".format(stdoutdata)
399
400 self.payload_file = payload_file
401 self.payload_properties = None
402
403 def Sign(self, payload_signer):
404 """Generates and signs the hashes of the payload and metadata.
405
406 Args:
407 payload_signer: A PayloadSigner() instance that serves the signing work.
408
409 Raises:
410 AssertionError: On any failure when calling brillo_update_payload script.
411 """
412 assert isinstance(payload_signer, PayloadSigner)
413
414 # 1. Generate hashes of the payload and metadata files.
415 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
416 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
417 cmd = ["brillo_update_payload", "hash",
418 "--unsigned_payload", self.payload_file,
419 "--signature_size", "256",
420 "--metadata_hash_file", metadata_sig_file,
421 "--payload_hash_file", payload_sig_file]
422 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
423 p1.communicate()
424 assert p1.returncode == 0, "brillo_update_payload hash failed"
425
426 # 2. Sign the hashes.
427 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
428 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
429
430 # 3. Insert the signatures back into the payload file.
431 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
432 suffix=".bin")
433 cmd = ["brillo_update_payload", "sign",
434 "--unsigned_payload", self.payload_file,
435 "--payload", signed_payload_file,
436 "--signature_size", "256",
437 "--metadata_signature_file", signed_metadata_sig_file,
438 "--payload_signature_file", signed_payload_sig_file]
439 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
440 p1.communicate()
441 assert p1.returncode == 0, "brillo_update_payload sign failed"
442
443 # 4. Dump the signed payload properties.
444 properties_file = common.MakeTempFile(prefix="payload-properties-",
445 suffix=".txt")
446 cmd = ["brillo_update_payload", "properties",
447 "--payload", signed_payload_file,
448 "--properties_file", properties_file]
449 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
450 p1.communicate()
451 assert p1.returncode == 0, "brillo_update_payload properties failed"
452
453 if OPTIONS.wipe_user_data:
454 with open(properties_file, "a") as f:
455 f.write("POWERWASH=1\n")
456
457 self.payload_file = signed_payload_file
458 self.payload_properties = properties_file
459
460 def WriteToZip(self, output_zip):
461 """Writes the payload to the given zip.
462
463 Args:
464 output_zip: The output ZipFile instance.
465 """
466 assert self.payload_file is not None
467 assert self.payload_properties is not None
468
469 # Add the signed payload file and properties into the zip. In order to
470 # support streaming, we pack them as ZIP_STORED. So these entries can be
471 # read directly with the offset and length pairs.
472 common.ZipWrite(output_zip, self.payload_file, arcname=Payload.PAYLOAD_BIN,
473 compress_type=zipfile.ZIP_STORED)
474 common.ZipWrite(output_zip, self.payload_properties,
475 arcname=Payload.PAYLOAD_PROPERTIES_TXT,
476 compress_type=zipfile.ZIP_STORED)
477
478
Doug Zongkereef39442009-04-02 12:14:19 -0700479def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200480 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700481
Doug Zongker951495f2009-08-14 12:44:19 -0700482 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
483 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700484
485
Tao Bao481bab82017-12-21 11:23:09 -0800486def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800487 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800488 if not oem_source:
489 return None
490
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800491 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800492 for oem_file in oem_source:
493 with open(oem_file) as fp:
494 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800495 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700496
Doug Zongkereef39442009-04-02 12:14:19 -0700497
Tao Baod42e97e2016-11-30 12:11:57 -0800498def _WriteRecoveryImageToBoot(script, output_zip):
499 """Find and write recovery image to /boot in two-step OTA.
500
501 In two-step OTAs, we write recovery image to /boot as the first step so that
502 we can reboot to there and install a new recovery image to /recovery.
503 A special "recovery-two-step.img" will be preferred, which encodes the correct
504 path of "/boot". Otherwise the device may show "device is corrupt" message
505 when booting into /boot.
506
507 Fall back to using the regular recovery.img if the two-step recovery image
508 doesn't exist. Note that rebuilding the special image at this point may be
509 infeasible, because we don't have the desired boot signer and keys when
510 calling ota_from_target_files.py.
511 """
512
513 recovery_two_step_img_name = "recovery-two-step.img"
514 recovery_two_step_img_path = os.path.join(
515 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
516 if os.path.exists(recovery_two_step_img_path):
517 recovery_two_step_img = common.GetBootableImage(
518 recovery_two_step_img_name, recovery_two_step_img_name,
519 OPTIONS.input_tmp, "RECOVERY")
520 common.ZipWriteStr(
521 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800522 print("two-step package: using %s in stage 1/3" % (
523 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800524 script.WriteRawImage("/boot", recovery_two_step_img_name)
525 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800526 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800527 # The "recovery.img" entry has been written into package earlier.
528 script.WriteRawImage("/boot", "recovery.img")
529
530
Doug Zongkerc9253822014-02-04 12:17:58 -0800531def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700532 namelist = [name for name in target_files_zip.namelist()]
533 return ("SYSTEM/recovery-from-boot.p" in namelist or
534 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700535
Tao Bao457cbf62017-03-06 09:56:01 -0800536
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700537def HasVendorPartition(target_files_zip):
538 try:
539 target_files_zip.getinfo("VENDOR/")
540 return True
541 except KeyError:
542 return False
543
Tao Bao457cbf62017-03-06 09:56:01 -0800544
Tao Bao481bab82017-12-21 11:23:09 -0800545def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700546 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800547 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700548
549
Tao Bao481bab82017-12-21 11:23:09 -0800550def WriteFingerprintAssertion(script, target_info, source_info):
551 source_oem_props = source_info.oem_props
552 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700553
Tao Bao481bab82017-12-21 11:23:09 -0800554 if source_oem_props is None and target_oem_props is None:
555 script.AssertSomeFingerprint(
556 source_info.fingerprint, target_info.fingerprint)
557 elif source_oem_props is not None and target_oem_props is not None:
558 script.AssertSomeThumbprint(
559 target_info.GetBuildProp("ro.build.thumbprint"),
560 source_info.GetBuildProp("ro.build.thumbprint"))
561 elif source_oem_props is None and target_oem_props is not None:
562 script.AssertFingerprintOrThumbprint(
563 source_info.fingerprint,
564 target_info.GetBuildProp("ro.build.thumbprint"))
565 else:
566 script.AssertFingerprintOrThumbprint(
567 target_info.fingerprint,
568 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700569
Doug Zongkerfc44a512014-08-26 13:10:25 -0700570
Tao Bao7e0f1602017-03-06 15:50:08 -0800571def GetImage(which, tmpdir):
572 """Returns an image object suitable for passing to BlockImageDiff.
573
574 'which' partition must be "system" or "vendor". A prebuilt image and file
575 map must already exist in tmpdir.
576 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700577
578 assert which in ("system", "vendor")
579
580 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700581 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
Doug Zongker3c84f562014-07-31 11:06:30 -0700582
Tao Bao7e0f1602017-03-06 15:50:08 -0800583 # The image and map files must have been created prior to calling
584 # ota_from_target_files.py (since LMP).
585 assert os.path.exists(path) and os.path.exists(mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700586
Tao Baoff777812015-05-12 11:42:31 -0700587 # Bug: http://b/20939131
588 # In ext4 filesystems, block 0 might be changed even being mounted
589 # R/O. We add it to clobbered_blocks so that it will be written to the
590 # target unconditionally. Note that they are still part of care_map.
591 clobbered_blocks = "0"
592
593 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700594
595
Tao Bao481bab82017-12-21 11:23:09 -0800596def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
597 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700598 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700599
600 Metadata used for on-device compatibility verification is retrieved from
601 target_zip then added to compatibility.zip which is added to the output_zip
602 archive.
603
Tao Baobcd1d162017-08-26 13:10:26 -0700604 Compatibility archive should only be included for devices that have enabled
605 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700606
607 Args:
608 target_zip: Zip file containing the source files to be included for OTA.
609 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800610 target_info: The BuildInfo instance that holds the target build info.
611 source_info: The BuildInfo instance that holds the source build info, if
612 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700613 """
614
Tao Baobcd1d162017-08-26 13:10:26 -0700615 def AddCompatibilityArchive(system_updated, vendor_updated):
616 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700617
Tao Baobcd1d162017-08-26 13:10:26 -0700618 Args:
619 system_updated: If True, the system image will be updated and therefore
620 its metadata should be included.
621 vendor_updated: If True, the vendor image will be updated and therefore
622 its metadata should be included.
623 """
624 # Determine what metadata we need. Files are names relative to META/.
625 compatibility_files = []
626 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
627 system_metadata = ("system_manifest.xml", "system_matrix.xml")
628 if vendor_updated:
629 compatibility_files += vendor_metadata
630 if system_updated:
631 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700632
Tao Baobcd1d162017-08-26 13:10:26 -0700633 # Create new archive.
634 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800635 compatibility_archive_zip = zipfile.ZipFile(
636 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700637
Tao Baobcd1d162017-08-26 13:10:26 -0700638 # Add metadata.
639 for file_name in compatibility_files:
640 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700641
Tao Baobcd1d162017-08-26 13:10:26 -0700642 if target_file_name in target_zip.namelist():
643 data = target_zip.read(target_file_name)
644 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700645
Tao Baobcd1d162017-08-26 13:10:26 -0700646 # Ensure files are written before we copy into output_zip.
647 compatibility_archive_zip.close()
648
649 # Only add the archive if we have any compatibility info.
650 if compatibility_archive_zip.namelist():
651 common.ZipWrite(output_zip, compatibility_archive.name,
652 arcname="compatibility.zip",
653 compress_type=zipfile.ZIP_STORED)
654
655 # Will only proceed if the target has enabled the Treble support (as well as
656 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800657 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700658 return
659
660 # We don't support OEM thumbprint in Treble world (which calculates
661 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800662 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700663
664 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800665 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700666 AddCompatibilityArchive(True, True)
667 return
668
Tao Bao481bab82017-12-21 11:23:09 -0800669 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700670
Tao Bao481bab82017-12-21 11:23:09 -0800671 source_fp = source_info.fingerprint
672 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700673 system_updated = source_fp != target_fp
674
Tao Bao481bab82017-12-21 11:23:09 -0800675 source_fp_vendor = source_info.GetVendorBuildProp(
676 "ro.vendor.build.fingerprint")
677 target_fp_vendor = target_info.GetVendorBuildProp(
678 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700679 vendor_updated = source_fp_vendor != target_fp_vendor
680
681 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700682
683
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700684def WriteFullOTAPackage(input_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800685 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700686
Tao Bao481bab82017-12-21 11:23:09 -0800687 # We don't know what version it will be installed on top of. We expect the API
688 # just won't change very often. Similarly for fstab, it might have changed in
689 # the target build.
690 target_api_version = target_info["recovery_api_version"]
691 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700692
Tao Bao481bab82017-12-21 11:23:09 -0800693 if target_info.oem_props and not OPTIONS.oem_no_mount:
694 target_info.WriteMountOemScript(script)
695
Tao Baodf3a48b2018-01-10 16:30:43 -0800696 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700697
Doug Zongker05d3dea2009-06-22 11:32:31 -0700698 device_specific = common.DeviceSpecificParams(
699 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800700 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700701 output_zip=output_zip,
702 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700703 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700704 metadata=metadata,
705 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700706
Tao Bao457cbf62017-03-06 09:56:01 -0800707 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800708
Tao Bao481bab82017-12-21 11:23:09 -0800709 # Assertions (e.g. downgrade check, device properties check).
710 ts = target_info.GetBuildProp("ro.build.date.utc")
711 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700712 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700713
Tao Bao481bab82017-12-21 11:23:09 -0800714 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700715 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800716
717 # Two-step package strategy (in chronological order, which is *not*
718 # the order in which the generated script has things):
719 #
720 # if stage is not "2/3" or "3/3":
721 # write recovery image to boot partition
722 # set stage to "2/3"
723 # reboot to boot partition and restart recovery
724 # else if stage is "2/3":
725 # write recovery image to recovery partition
726 # set stage to "3/3"
727 # reboot to recovery partition and restart recovery
728 # else:
729 # (stage must be "3/3")
730 # set stage to ""
731 # do normal full package installation:
732 # wipe and install system, boot image, etc.
733 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700734 # complete script normally
735 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800736
737 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
738 OPTIONS.input_tmp, "RECOVERY")
739 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800740 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800741 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800742 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800743 assert fs.fs_type.upper() == "EMMC", \
744 "two-step packages only supported on devices with EMMC /misc partitions"
745 bcb_dev = {"bcb_dev": fs.device}
746 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
747 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700748if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800749""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800750
751 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
752 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800753 script.WriteRawImage("/recovery", "recovery.img")
754 script.AppendExtra("""
755set_stage("%(bcb_dev)s", "3/3");
756reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700757else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800758""" % bcb_dev)
759
Tao Baod42e97e2016-11-30 12:11:57 -0800760 # Stage 3/3: Make changes.
761 script.Comment("Stage 3/3")
762
Tao Bao6c55a8a2015-04-08 15:30:27 -0700763 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800764 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700765
Doug Zongkere5ff5902012-01-17 10:55:37 -0800766 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700767
Doug Zongker01ce19c2014-02-04 13:48:15 -0800768 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700769
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700770 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800771 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700772 if HasVendorPartition(input_zip):
773 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700774
Doug Zongker4b9596f2014-06-09 14:15:45 -0700775 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800776
Tao Bao457cbf62017-03-06 09:56:01 -0800777 # Full OTA is done as an "incremental" against an empty source image. This
778 # has the effect of writing new data from the package to the entire
779 # partition, but lets us reuse the updater code that writes incrementals to
780 # do it.
781 system_tgt = GetImage("system", OPTIONS.input_tmp)
782 system_tgt.ResetFileMap()
783 system_diff = common.BlockDifference("system", system_tgt, src=None)
784 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700785
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700786 boot_img = common.GetBootableImage(
787 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800788
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700789 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700790 script.ShowProgress(0.1, 0)
791
Tao Bao457cbf62017-03-06 09:56:01 -0800792 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
793 vendor_tgt.ResetFileMap()
794 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
795 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700796
Tao Bao481bab82017-12-21 11:23:09 -0800797 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700798
Tao Bao481bab82017-12-21 11:23:09 -0800799 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700800 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700801
Doug Zongker01ce19c2014-02-04 13:48:15 -0800802 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700803 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700804
Doug Zongker01ce19c2014-02-04 13:48:15 -0800805 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700806 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700807
Doug Zongker1c390a22009-05-14 19:06:36 -0700808 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700809 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700810
Doug Zongker14833602010-02-02 13:12:04 -0800811 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800812
Doug Zongker922206e2014-03-04 13:16:24 -0800813 if OPTIONS.wipe_user_data:
814 script.ShowProgress(0.1, 10)
815 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700816
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800817 if OPTIONS.two_step:
818 script.AppendExtra("""
819set_stage("%(bcb_dev)s", "");
820""" % bcb_dev)
821 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800822
823 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
824 script.Comment("Stage 1/3")
825 _WriteRecoveryImageToBoot(script, output_zip)
826
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800827 script.AppendExtra("""
828set_stage("%(bcb_dev)s", "2/3");
829reboot_now("%(bcb_dev)s", "");
830endif;
831endif;
832""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800833
Tao Bao5d182562016-02-23 11:38:39 -0800834 script.SetProgress(1)
835 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800836 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700837 WriteMetadata(metadata, output_zip)
838
Doug Zongkerfc44a512014-08-26 13:10:25 -0700839
Doug Zongker2ea21062010-04-28 16:05:21 -0700840def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800841 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
842 common.ZipWriteStr(output_zip, METADATA_NAME, value,
843 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700844
Doug Zongkerfc44a512014-08-26 13:10:25 -0700845
Tao Bao481bab82017-12-21 11:23:09 -0800846def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800847 # Only incremental OTAs are allowed to reach here.
848 assert OPTIONS.incremental_source is not None
849
Tao Bao481bab82017-12-21 11:23:09 -0800850 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
851 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800852 is_downgrade = long(post_timestamp) < long(pre_timestamp)
853
854 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800855 if not is_downgrade:
856 raise RuntimeError("--downgrade specified but no downgrade detected: "
857 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800858 metadata["ota-downgrade"] = "yes"
859 elif OPTIONS.timestamp:
860 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800861 raise RuntimeError("--override_timestamp specified but no timestamp hack "
862 "needed: pre: %s, post: %s" % (pre_timestamp,
863 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800864 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800865 else:
866 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800867 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800868 "pre: %s, post: %s. Need to specify "
869 "--override_timestamp OR --downgrade to allow "
870 "building the incremental." % (pre_timestamp,
871 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800872 metadata["post-timestamp"] = post_timestamp
873
874
Tao Baodf3a48b2018-01-10 16:30:43 -0800875def GetPackageMetadata(target_info, source_info=None):
876 """Generates and returns the metadata dict.
877
878 It generates a dict() that contains the info to be written into an OTA
879 package (META-INF/com/android/metadata). It also handles the detection of
880 downgrade / timestamp override / data wipe based on the global options.
881
882 Args:
883 target_info: The BuildInfo instance that holds the target build info.
884 source_info: The BuildInfo instance that holds the source build info, or
885 None if generating full OTA.
886
887 Returns:
888 A dict to be written into package metadata entry.
889 """
890 assert isinstance(target_info, BuildInfo)
891 assert source_info is None or isinstance(source_info, BuildInfo)
892
893 metadata = {
894 'post-build' : target_info.fingerprint,
895 'post-build-incremental' : target_info.GetBuildProp(
896 'ro.build.version.incremental'),
897 }
898
899 if target_info.is_ab:
900 metadata['ota-type'] = 'AB'
901 metadata['ota-required-cache'] = '0'
902 else:
903 metadata['ota-type'] = 'BLOCK'
904
905 if OPTIONS.wipe_user_data:
906 metadata['ota-wipe'] = 'yes'
907
908 is_incremental = source_info is not None
909 if is_incremental:
910 metadata['pre-build'] = source_info.fingerprint
911 metadata['pre-build-incremental'] = source_info.GetBuildProp(
912 'ro.build.version.incremental')
913 metadata['pre-device'] = source_info.device
914 else:
915 metadata['pre-device'] = target_info.device
916
917 # Detect downgrades, or fill in the post-timestamp.
918 if is_incremental:
919 HandleDowngradeMetadata(metadata, target_info, source_info)
920 else:
921 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
922
923 return metadata
924
925
Geremy Condra36bd3652014-02-06 19:45:10 -0800926def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800927 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
928 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800929
Tao Bao481bab82017-12-21 11:23:09 -0800930 target_api_version = target_info["recovery_api_version"]
931 source_api_version = source_info["recovery_api_version"]
932 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700933 print("WARNING: generating edify script for a source that "
934 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800935
Tao Bao481bab82017-12-21 11:23:09 -0800936 script = edify_generator.EdifyGenerator(
937 source_api_version, target_info, fstab=source_info["fstab"])
938
939 if target_info.oem_props or source_info.oem_props:
940 if not OPTIONS.oem_no_mount:
941 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -0700942
Tao Baodf3a48b2018-01-10 16:30:43 -0800943 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -0800944
Geremy Condra36bd3652014-02-06 19:45:10 -0800945 device_specific = common.DeviceSpecificParams(
946 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800947 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800948 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800949 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800950 output_zip=output_zip,
951 script=script,
952 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -0800953 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800954
Geremy Condra36bd3652014-02-06 19:45:10 -0800955 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800956 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800957 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800958 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800959 updating_boot = (not OPTIONS.two_step and
960 (source_boot.data != target_boot.data))
961
Geremy Condra36bd3652014-02-06 19:45:10 -0800962 target_recovery = common.GetBootableImage(
963 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800964
Tao Bao7e0f1602017-03-06 15:50:08 -0800965 system_src = GetImage("system", OPTIONS.source_tmp)
966 system_tgt = GetImage("system", OPTIONS.target_tmp)
Tao Baodd2a5892015-03-12 12:32:37 -0700967
Tao Bao0582cb62017-12-21 11:47:01 -0800968 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -0800969 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -0800970 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -0700971
Tao Baof8acad12016-07-07 09:09:58 -0700972 # Check the first block of the source system partition for remount R/W only
973 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -0800974 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700975 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700976 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
977 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
978 # b) the blocks listed in block map may not contain all the bytes for a given
979 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -0800980 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700981 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
982 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700983 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800984 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700985 version=blockimgdiff_version,
986 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700987
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700988 if HasVendorPartition(target_zip):
989 if not HasVendorPartition(source_zip):
990 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Bao7e0f1602017-03-06 15:50:08 -0800991 vendor_src = GetImage("vendor", OPTIONS.source_tmp)
992 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800993
994 # Check first block of vendor partition for remount R/W only if
995 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -0800996 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800997 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700998 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700999 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001000 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001001 version=blockimgdiff_version,
1002 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001003 else:
1004 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001005
Tao Baobcd1d162017-08-26 13:10:26 -07001006 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001007 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001008
Tao Bao481bab82017-12-21 11:23:09 -08001009 # Assertions (e.g. device properties check).
1010 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001011 device_specific.IncrementalOTA_Assertions()
1012
1013 # Two-step incremental package strategy (in chronological order,
1014 # which is *not* the order in which the generated script has
1015 # things):
1016 #
1017 # if stage is not "2/3" or "3/3":
1018 # do verification on current system
1019 # write recovery image to boot partition
1020 # set stage to "2/3"
1021 # reboot to boot partition and restart recovery
1022 # else if stage is "2/3":
1023 # write recovery image to recovery partition
1024 # set stage to "3/3"
1025 # reboot to recovery partition and restart recovery
1026 # else:
1027 # (stage must be "3/3")
1028 # perform update:
1029 # patch system files, etc.
1030 # force full install of new boot image
1031 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001032 # complete script normally
1033 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001034
1035 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001036 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001037 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001038 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001039 assert fs.fs_type.upper() == "EMMC", \
1040 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001041 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001042 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1043 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001044if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001045""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001046
1047 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1048 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001049 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001050 script.WriteRawImage("/recovery", "recovery.img")
1051 script.AppendExtra("""
1052set_stage("%(bcb_dev)s", "3/3");
1053reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001054else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001055""" % bcb_dev)
1056
Tao Baod42e97e2016-11-30 12:11:57 -08001057 # Stage 1/3: (a) Verify the current system.
1058 script.Comment("Stage 1/3")
1059
Tao Bao6c55a8a2015-04-08 15:30:27 -07001060 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001061 script.Print("Source: {}".format(source_info.fingerprint))
1062 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001063
Geremy Condra36bd3652014-02-06 19:45:10 -08001064 script.Print("Verifying current system...")
1065
1066 device_specific.IncrementalOTA_VerifyBegin()
1067
Tao Bao481bab82017-12-21 11:23:09 -08001068 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001069
Tao Baod8d14be2016-02-04 14:26:02 -08001070 # Check the required cache size (i.e. stashed blocks).
1071 size = []
1072 if system_diff:
1073 size.append(system_diff.required_cache)
1074 if vendor_diff:
1075 size.append(vendor_diff.required_cache)
1076
Geremy Condra36bd3652014-02-06 19:45:10 -08001077 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001078 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001079 d = common.Difference(target_boot, source_boot)
1080 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001081 if d is None:
1082 include_full_boot = True
1083 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1084 else:
1085 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001086
Tao Bao89fbb0f2017-01-10 10:47:58 -08001087 print("boot target: %d source: %d diff: %d" % (
1088 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001089
Doug Zongkerf8340082014-08-05 10:39:37 -07001090 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001091
Doug Zongkerf8340082014-08-05 10:39:37 -07001092 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1093 (boot_type, boot_device,
1094 source_boot.size, source_boot.sha1,
1095 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001096 size.append(target_boot.size)
1097
1098 if size:
1099 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001100
1101 device_specific.IncrementalOTA_VerifyEnd()
1102
1103 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001104 # Stage 1/3: (b) Write recovery image to /boot.
1105 _WriteRecoveryImageToBoot(script, output_zip)
1106
Geremy Condra36bd3652014-02-06 19:45:10 -08001107 script.AppendExtra("""
1108set_stage("%(bcb_dev)s", "2/3");
1109reboot_now("%(bcb_dev)s", "");
1110else
1111""" % bcb_dev)
1112
Tao Baod42e97e2016-11-30 12:11:57 -08001113 # Stage 3/3: Make changes.
1114 script.Comment("Stage 3/3")
1115
Jesse Zhao75bcea02015-01-06 10:59:53 -08001116 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001117 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001118 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001119 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001120
Geremy Condra36bd3652014-02-06 19:45:10 -08001121 script.Comment("---- start making changes here ----")
1122
1123 device_specific.IncrementalOTA_InstallBegin()
1124
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001125 system_diff.WriteScript(script, output_zip,
1126 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001127
Doug Zongkerfc44a512014-08-26 13:10:25 -07001128 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001129 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001130
1131 if OPTIONS.two_step:
1132 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1133 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001134 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001135
1136 if not OPTIONS.two_step:
1137 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001138 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001139 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001140 script.Print("Installing boot image...")
1141 script.WriteRawImage("/boot", "boot.img")
1142 else:
1143 # Produce the boot image by applying a patch to the current
1144 # contents of the boot partition, and write it back to the
1145 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001146 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001147 script.Print("Patching boot image...")
1148 script.ShowProgress(0.1, 10)
1149 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1150 % (boot_type, boot_device,
1151 source_boot.size, source_boot.sha1,
1152 target_boot.size, target_boot.sha1),
1153 "-",
1154 target_boot.size, target_boot.sha1,
1155 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001156 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001157 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001158
1159 # Do device-specific installation (eg, write radio image).
1160 device_specific.IncrementalOTA_InstallEnd()
1161
1162 if OPTIONS.extra_script is not None:
1163 script.AppendExtra(OPTIONS.extra_script)
1164
Doug Zongker922206e2014-03-04 13:16:24 -08001165 if OPTIONS.wipe_user_data:
1166 script.Print("Erasing user data...")
1167 script.FormatPartition("/data")
1168
Geremy Condra36bd3652014-02-06 19:45:10 -08001169 if OPTIONS.two_step:
1170 script.AppendExtra("""
1171set_stage("%(bcb_dev)s", "");
1172endif;
1173endif;
1174""" % bcb_dev)
1175
1176 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001177 # For downgrade OTAs, we prefer to use the update-binary in the source
1178 # build that is actually newer than the one in the target build.
1179 if OPTIONS.downgrade:
1180 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1181 else:
1182 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001183 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001184 WriteMetadata(metadata, output_zip)
1185
Doug Zongker32b527d2014-03-04 10:03:02 -08001186
Tao Baoc098e9e2016-01-07 13:03:56 -08001187def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1188 source_file=None):
1189 """Generate an Android OTA package that has A/B update payload."""
1190
Tao Bao2dd1c482017-02-03 16:49:39 -08001191 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1192 expected_length=None):
1193 """Compute the streaming metadata for a given zip.
1194
1195 When 'reserve_space' is True, we reserve extra space for the offset and
1196 length of the metadata entry itself, although we don't know the final
1197 values until the package gets signed. This function will be called again
1198 after signing. We then write the actual values and pad the string to the
1199 length we set earlier. Note that we can't use the actual length of the
1200 metadata entry in the second run. Otherwise the offsets for other entries
1201 will be changing again.
1202 """
Tao Baoc96316c2017-01-24 22:10:49 -08001203
1204 def ComputeEntryOffsetSize(name):
1205 """Compute the zip entry offset and size."""
1206 info = zip_file.getinfo(name)
1207 offset = info.header_offset + len(info.FileHeader())
1208 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001209 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001210
1211 # payload.bin and payload_properties.txt must exist.
1212 offsets = [ComputeEntryOffsetSize('payload.bin'),
1213 ComputeEntryOffsetSize('payload_properties.txt')]
1214
1215 # care_map.txt is available only if dm-verity is enabled.
1216 if 'care_map.txt' in zip_file.namelist():
1217 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001218
Tao Bao21803d32017-04-19 10:16:09 -07001219 if 'compatibility.zip' in zip_file.namelist():
1220 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1221
Tao Bao2dd1c482017-02-03 16:49:39 -08001222 # 'META-INF/com/android/metadata' is required. We don't know its actual
1223 # offset and length (as well as the values for other entries). So we
1224 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1225 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1226 # beginning of the zip), as well as the possible value changes in other
1227 # entries.
1228 if reserve_space:
1229 offsets.append('metadata:' + ' ' * 10)
1230 else:
1231 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1232
1233 value = ','.join(offsets)
1234 if expected_length is not None:
1235 assert len(value) <= expected_length, \
1236 'Insufficient reserved space: reserved=%d, actual=%d' % (
1237 expected_length, len(value))
1238 value += ' ' * (expected_length - len(value))
1239 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001240
Tao Baodea0f8b2016-06-20 17:55:06 -07001241 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001242 temp_zip_file = tempfile.NamedTemporaryFile()
1243 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1244 compression=zipfile.ZIP_DEFLATED)
1245
Tao Bao481bab82017-12-21 11:23:09 -08001246 if source_file is not None:
1247 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1248 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1249 else:
1250 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1251 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001252
Tao Bao481bab82017-12-21 11:23:09 -08001253 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001254 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001255
Tao Baoc7b403a2018-01-30 18:19:04 -08001256 # Generate payload.
1257 payload = Payload()
1258 payload.Generate(target_file, source_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001259
Tao Baoc7b403a2018-01-30 18:19:04 -08001260 # Sign the payload.
1261 payload.Sign(PayloadSigner())
Tao Baoc098e9e2016-01-07 13:03:56 -08001262
Tao Baoc7b403a2018-01-30 18:19:04 -08001263 # Write the payload into output zip.
1264 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001265
Tianjie Xucfa86222016-03-07 16:31:19 -08001266 # If dm-verity is supported for the device, copy contents of care_map
1267 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001268 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001269 if (target_info.get("verity") == "true" or
1270 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001271 care_map_path = "META/care_map.txt"
1272 namelist = target_zip.namelist()
1273 if care_map_path in namelist:
1274 care_map_data = target_zip.read(care_map_path)
Tao Baoc7b403a2018-01-30 18:19:04 -08001275 # In order to support streaming, care_map.txt needs to be packed as
1276 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001277 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001278 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001279 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001280 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001281
Tao Bao481bab82017-12-21 11:23:09 -08001282 # source_info must be None for full OTAs.
Tao Baobcd1d162017-08-26 13:10:26 -07001283 if source_file is None:
Tao Bao481bab82017-12-21 11:23:09 -08001284 assert source_info is None
Tao Bao21803d32017-04-19 10:16:09 -07001285
Tao Baobcd1d162017-08-26 13:10:26 -07001286 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001287 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001288
Tao Bao21803d32017-04-19 10:16:09 -07001289 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001290
Tao Bao2dd1c482017-02-03 16:49:39 -08001291 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001292 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001293 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001294 WriteMetadata(metadata, output_zip)
1295 common.ZipClose(output_zip)
1296
Tao Bao2dd1c482017-02-03 16:49:39 -08001297 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
Tao Bao89d7ab22017-12-14 17:05:33 -08001298 # ZIP entries, as well as padding the entry headers. We do a preliminary
Tao Bao2dd1c482017-02-03 16:49:39 -08001299 # signing (with an incomplete metadata entry) to allow that to happen. Then
Tao Bao89d7ab22017-12-14 17:05:33 -08001300 # compute the ZIP entry offsets, write back the final metadata and do the
Tao Bao2dd1c482017-02-03 16:49:39 -08001301 # final signing.
Tao Bao89d7ab22017-12-14 17:05:33 -08001302 prelim_signing = common.MakeTempFile(suffix='.zip')
1303 SignOutput(temp_zip_file.name, prelim_signing)
Tao Bao2dd1c482017-02-03 16:49:39 -08001304 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001305
Tao Bao2dd1c482017-02-03 16:49:39 -08001306 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Bao89d7ab22017-12-14 17:05:33 -08001307 prelim_signing_zip = zipfile.ZipFile(prelim_signing, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001308 expected_length = len(metadata['ota-streaming-property-files'])
1309 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao89d7ab22017-12-14 17:05:33 -08001310 prelim_signing_zip, reserve_space=False, expected_length=expected_length)
1311 common.ZipClose(prelim_signing_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001312
Tao Bao89d7ab22017-12-14 17:05:33 -08001313 # Replace the METADATA entry.
1314 common.ZipDelete(prelim_signing, METADATA_NAME)
1315 output_zip = zipfile.ZipFile(prelim_signing, 'a',
Tao Bao2dd1c482017-02-03 16:49:39 -08001316 compression=zipfile.ZIP_DEFLATED)
Tao Bao2dd1c482017-02-03 16:49:39 -08001317 WriteMetadata(metadata, output_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001318 common.ZipClose(output_zip)
1319
1320 # Re-sign the package after updating the metadata entry.
Tao Bao89d7ab22017-12-14 17:05:33 -08001321 SignOutput(prelim_signing, output_file)
Tao Bao2dd1c482017-02-03 16:49:39 -08001322
1323 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao89d7ab22017-12-14 17:05:33 -08001324 output_zip = zipfile.ZipFile(output_file, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001325 actual = metadata['ota-streaming-property-files'].strip()
1326 expected = ComputeStreamingMetadata(output_zip)
1327 assert actual == expected, \
1328 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001329 common.ZipClose(output_zip)
1330
Tao Baoc098e9e2016-01-07 13:03:56 -08001331
Doug Zongkereef39442009-04-02 12:14:19 -07001332def main(argv):
1333
1334 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001335 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001336 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001337 elif o in ("-i", "--incremental_from"):
1338 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001339 elif o == "--full_radio":
1340 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001341 elif o == "--full_bootloader":
1342 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001343 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001344 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001345 elif o == "--downgrade":
1346 OPTIONS.downgrade = True
1347 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001348 elif o == "--override_timestamp":
1349 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001350 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001351 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001352 elif o == "--oem_no_mount":
1353 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001354 elif o in ("-e", "--extra_script"):
1355 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001356 elif o in ("-t", "--worker_threads"):
1357 if a.isdigit():
1358 OPTIONS.worker_threads = int(a)
1359 else:
1360 raise ValueError("Cannot parse value %r for option %r - only "
1361 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001362 elif o in ("-2", "--two_step"):
1363 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001364 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001365 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001366 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001367 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001368 elif o == "--block":
1369 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001370 elif o in ("-b", "--binary"):
1371 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001372 elif o == "--stash_threshold":
1373 try:
1374 OPTIONS.stash_threshold = float(a)
1375 except ValueError:
1376 raise ValueError("Cannot parse value %r for option %r - expecting "
1377 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001378 elif o == "--log_diff":
1379 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001380 elif o == "--payload_signer":
1381 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001382 elif o == "--payload_signer_args":
1383 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001384 elif o == "--extracted_input_target_files":
1385 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001386 else:
1387 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001388 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001389
1390 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001391 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001392 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001393 "package_key=",
1394 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001395 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001396 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001397 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001398 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001399 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001400 "extra_script=",
1401 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001402 "two_step",
1403 "no_signing",
1404 "block",
1405 "binary=",
1406 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001407 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001408 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001409 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001410 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001411 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001412 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001413 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001414 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001415
1416 if len(args) != 2:
1417 common.Usage(__doc__)
1418 sys.exit(1)
1419
Tao Bao5d182562016-02-23 11:38:39 -08001420 if OPTIONS.downgrade:
1421 # Sanity check to enforce a data wipe.
1422 if not OPTIONS.wipe_user_data:
1423 raise ValueError("Cannot downgrade without a data wipe")
1424
1425 # We should only allow downgrading incrementals (as opposed to full).
1426 # Otherwise the device may go back from arbitrary build with this full
1427 # OTA package.
1428 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001429 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001430
Tao Bao3e6161a2017-02-28 11:48:48 -08001431 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1432 "Cannot have --downgrade AND --override_timestamp both"
1433
Tao Bao2db13852018-01-08 22:28:57 -08001434 # Load the build info dicts from the zip directly or the extracted input
1435 # directory. We don't need to unzip the entire target-files zips, because they
1436 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1437 # When loading the info dicts, we don't need to provide the second parameter
1438 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1439 # some properties with their actual paths, such as 'selinux_fc',
1440 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001441 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001442 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001443 else:
Tao Bao2db13852018-01-08 22:28:57 -08001444 with zipfile.ZipFile(args[0], 'r') as input_zip:
1445 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001446
Tao Bao2db13852018-01-08 22:28:57 -08001447 if OPTIONS.verbose:
1448 print("--- target info ---")
1449 common.DumpInfoDict(OPTIONS.info_dict)
1450
1451 # Load the source build dict if applicable.
1452 if OPTIONS.incremental_source is not None:
1453 OPTIONS.target_info_dict = OPTIONS.info_dict
1454 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1455 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1456
1457 if OPTIONS.verbose:
1458 print("--- source info ---")
1459 common.DumpInfoDict(OPTIONS.source_info_dict)
1460
1461 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001462 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1463
Tao Baoc098e9e2016-01-07 13:03:56 -08001464 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1465
Christian Oderf63e2cd2017-05-01 22:30:15 +02001466 # Use the default key to sign the package if not specified with package_key.
1467 # package_keys are needed on ab_updates, so always define them if an
1468 # ab_update is getting created.
1469 if not OPTIONS.no_signing or ab_update:
1470 if OPTIONS.package_key is None:
1471 OPTIONS.package_key = OPTIONS.info_dict.get(
1472 "default_system_dev_certificate",
1473 "build/target/product/security/testkey")
1474 # Get signing keys
1475 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1476
Tao Baoc098e9e2016-01-07 13:03:56 -08001477 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001478 WriteABOTAPackageWithBrilloScript(
1479 target_file=args[0],
1480 output_file=args[1],
1481 source_file=OPTIONS.incremental_source)
1482
Tao Bao89fbb0f2017-01-10 10:47:58 -08001483 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001484 return
1485
Tao Bao2db13852018-01-08 22:28:57 -08001486 # Sanity check the loaded info dicts first.
1487 if OPTIONS.info_dict.get("no_recovery") == "true":
1488 raise common.ExternalError(
1489 "--- target build has specified no recovery ---")
1490
1491 # Non-A/B OTAs rely on /cache partition to store temporary files.
1492 cache_size = OPTIONS.info_dict.get("cache_size")
1493 if cache_size is None:
1494 print("--- can't determine the cache partition size ---")
1495 OPTIONS.cache_size = cache_size
1496
Doug Zongker1c390a22009-05-14 19:06:36 -07001497 if OPTIONS.extra_script is not None:
1498 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1499
Dan Willemsencea5cd22017-03-21 14:44:27 -07001500 if OPTIONS.extracted_input is not None:
1501 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001502 input_zip = zipfile.ZipFile(args[0], "r")
1503 else:
1504 print("unzipping target target-files...")
1505 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1506 args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001507 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001508
Tao Bao2db13852018-01-08 22:28:57 -08001509 # If the caller explicitly specified the device-specific extensions path via
1510 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1511 # is present in the target target_files. Otherwise, take the path of the file
1512 # from 'tool_extensions' in the info dict and look for that in the local
1513 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001514 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001515 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1516 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001517 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001518 OPTIONS.device_specific = from_input
1519 else:
Tao Bao2db13852018-01-08 22:28:57 -08001520 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001521
Doug Zongker37974732010-09-16 17:44:38 -07001522 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001523 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001524
Tao Bao767e3ac2015-11-10 12:19:19 -08001525 # Set up the output zip. Create a temporary zip file if signing is needed.
1526 if OPTIONS.no_signing:
1527 if os.path.exists(args[1]):
1528 os.unlink(args[1])
1529 output_zip = zipfile.ZipFile(args[1], "w",
1530 compression=zipfile.ZIP_DEFLATED)
1531 else:
1532 temp_zip_file = tempfile.NamedTemporaryFile()
1533 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1534 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001535
Tao Bao767e3ac2015-11-10 12:19:19 -08001536 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001537 if OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001538 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001539
Tao Bao32b80dc2018-01-08 22:50:47 -08001540 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001541 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001542 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001543 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001544 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001545 UNZIP_PATTERN)
Tao Bao32b80dc2018-01-08 22:50:47 -08001546
1547 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
1548
1549 if OPTIONS.log_diff:
1550 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001551 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001552 target_files_diff.recursiveDiff(
1553 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001554
Tao Bao2db13852018-01-08 22:28:57 -08001555 common.ZipClose(input_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001556 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001557
Tao Bao767e3ac2015-11-10 12:19:19 -08001558 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001559 if not OPTIONS.no_signing:
1560 SignOutput(temp_zip_file.name, args[1])
1561 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001562
Tao Bao89fbb0f2017-01-10 10:47:58 -08001563 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001564
1565
1566if __name__ == '__main__':
1567 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001568 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001569 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001570 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001571 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001572 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001573 finally:
1574 common.Cleanup()