blob: 12b01c4b98c1e010789750670aa4f2858e2518d3 [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
Tao Baoc7b403a2018-01-30 18:19:04 -0800362class Payload(object):
363 """Manages the creation and the signing of an A/B OTA Payload."""
364
365 PAYLOAD_BIN = 'payload.bin'
366 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
367
368 def __init__(self):
369 # The place where the output from the subprocess should go.
370 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
371 self.payload_file = None
372 self.payload_properties = None
373
374 def Generate(self, target_file, source_file=None, additional_args=None):
375 """Generates a payload from the given target-files zip(s).
376
377 Args:
378 target_file: The filename of the target build target-files zip.
379 source_file: The filename of the source build target-files zip; or None if
380 generating a full OTA.
381 additional_args: A list of additional args that should be passed to
382 brillo_update_payload script; or None.
383 """
384 if additional_args is None:
385 additional_args = []
386
387 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
388 cmd = ["brillo_update_payload", "generate",
389 "--payload", payload_file,
390 "--target_image", target_file]
391 if source_file is not None:
392 cmd.extend(["--source_image", source_file])
393 cmd.extend(additional_args)
394 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
395 stdoutdata, _ = p.communicate()
396 assert p.returncode == 0, \
397 "brillo_update_payload generate failed: {}".format(stdoutdata)
398
399 self.payload_file = payload_file
400 self.payload_properties = None
401
402 def Sign(self, payload_signer):
403 """Generates and signs the hashes of the payload and metadata.
404
405 Args:
406 payload_signer: A PayloadSigner() instance that serves the signing work.
407
408 Raises:
409 AssertionError: On any failure when calling brillo_update_payload script.
410 """
411 assert isinstance(payload_signer, PayloadSigner)
412
413 # 1. Generate hashes of the payload and metadata files.
414 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
415 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
416 cmd = ["brillo_update_payload", "hash",
417 "--unsigned_payload", self.payload_file,
418 "--signature_size", "256",
419 "--metadata_hash_file", metadata_sig_file,
420 "--payload_hash_file", payload_sig_file]
421 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
422 p1.communicate()
423 assert p1.returncode == 0, "brillo_update_payload hash failed"
424
425 # 2. Sign the hashes.
426 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
427 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
428
429 # 3. Insert the signatures back into the payload file.
430 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
431 suffix=".bin")
432 cmd = ["brillo_update_payload", "sign",
433 "--unsigned_payload", self.payload_file,
434 "--payload", signed_payload_file,
435 "--signature_size", "256",
436 "--metadata_signature_file", signed_metadata_sig_file,
437 "--payload_signature_file", signed_payload_sig_file]
438 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
439 p1.communicate()
440 assert p1.returncode == 0, "brillo_update_payload sign failed"
441
442 # 4. Dump the signed payload properties.
443 properties_file = common.MakeTempFile(prefix="payload-properties-",
444 suffix=".txt")
445 cmd = ["brillo_update_payload", "properties",
446 "--payload", signed_payload_file,
447 "--properties_file", properties_file]
448 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
449 p1.communicate()
450 assert p1.returncode == 0, "brillo_update_payload properties failed"
451
452 if OPTIONS.wipe_user_data:
453 with open(properties_file, "a") as f:
454 f.write("POWERWASH=1\n")
455
456 self.payload_file = signed_payload_file
457 self.payload_properties = properties_file
458
459 def WriteToZip(self, output_zip):
460 """Writes the payload to the given zip.
461
462 Args:
463 output_zip: The output ZipFile instance.
464 """
465 assert self.payload_file is not None
466 assert self.payload_properties is not None
467
468 # Add the signed payload file and properties into the zip. In order to
469 # support streaming, we pack them as ZIP_STORED. So these entries can be
470 # read directly with the offset and length pairs.
471 common.ZipWrite(output_zip, self.payload_file, arcname=Payload.PAYLOAD_BIN,
472 compress_type=zipfile.ZIP_STORED)
473 common.ZipWrite(output_zip, self.payload_properties,
474 arcname=Payload.PAYLOAD_PROPERTIES_TXT,
475 compress_type=zipfile.ZIP_STORED)
476
477
Doug Zongkereef39442009-04-02 12:14:19 -0700478def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200479 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700480
Doug Zongker951495f2009-08-14 12:44:19 -0700481 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
482 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700483
484
Tao Bao481bab82017-12-21 11:23:09 -0800485def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800486 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800487 if not oem_source:
488 return None
489
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800490 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800491 for oem_file in oem_source:
492 with open(oem_file) as fp:
493 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800494 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700495
Doug Zongkereef39442009-04-02 12:14:19 -0700496
Tao Baod42e97e2016-11-30 12:11:57 -0800497def _WriteRecoveryImageToBoot(script, output_zip):
498 """Find and write recovery image to /boot in two-step OTA.
499
500 In two-step OTAs, we write recovery image to /boot as the first step so that
501 we can reboot to there and install a new recovery image to /recovery.
502 A special "recovery-two-step.img" will be preferred, which encodes the correct
503 path of "/boot". Otherwise the device may show "device is corrupt" message
504 when booting into /boot.
505
506 Fall back to using the regular recovery.img if the two-step recovery image
507 doesn't exist. Note that rebuilding the special image at this point may be
508 infeasible, because we don't have the desired boot signer and keys when
509 calling ota_from_target_files.py.
510 """
511
512 recovery_two_step_img_name = "recovery-two-step.img"
513 recovery_two_step_img_path = os.path.join(
514 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
515 if os.path.exists(recovery_two_step_img_path):
516 recovery_two_step_img = common.GetBootableImage(
517 recovery_two_step_img_name, recovery_two_step_img_name,
518 OPTIONS.input_tmp, "RECOVERY")
519 common.ZipWriteStr(
520 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800521 print("two-step package: using %s in stage 1/3" % (
522 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800523 script.WriteRawImage("/boot", recovery_two_step_img_name)
524 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800525 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800526 # The "recovery.img" entry has been written into package earlier.
527 script.WriteRawImage("/boot", "recovery.img")
528
529
Doug Zongkerc9253822014-02-04 12:17:58 -0800530def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700531 namelist = [name for name in target_files_zip.namelist()]
532 return ("SYSTEM/recovery-from-boot.p" in namelist or
533 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700534
Tao Bao457cbf62017-03-06 09:56:01 -0800535
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700536def HasVendorPartition(target_files_zip):
537 try:
538 target_files_zip.getinfo("VENDOR/")
539 return True
540 except KeyError:
541 return False
542
Tao Bao457cbf62017-03-06 09:56:01 -0800543
Tao Bao481bab82017-12-21 11:23:09 -0800544def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700545 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800546 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700547
548
Tao Bao481bab82017-12-21 11:23:09 -0800549def WriteFingerprintAssertion(script, target_info, source_info):
550 source_oem_props = source_info.oem_props
551 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700552
Tao Bao481bab82017-12-21 11:23:09 -0800553 if source_oem_props is None and target_oem_props is None:
554 script.AssertSomeFingerprint(
555 source_info.fingerprint, target_info.fingerprint)
556 elif source_oem_props is not None and target_oem_props is not None:
557 script.AssertSomeThumbprint(
558 target_info.GetBuildProp("ro.build.thumbprint"),
559 source_info.GetBuildProp("ro.build.thumbprint"))
560 elif source_oem_props is None and target_oem_props is not None:
561 script.AssertFingerprintOrThumbprint(
562 source_info.fingerprint,
563 target_info.GetBuildProp("ro.build.thumbprint"))
564 else:
565 script.AssertFingerprintOrThumbprint(
566 target_info.fingerprint,
567 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700568
Doug Zongkerfc44a512014-08-26 13:10:25 -0700569
Tao Bao481bab82017-12-21 11:23:09 -0800570def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
571 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700572 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700573
574 Metadata used for on-device compatibility verification is retrieved from
575 target_zip then added to compatibility.zip which is added to the output_zip
576 archive.
577
Tao Baobcd1d162017-08-26 13:10:26 -0700578 Compatibility archive should only be included for devices that have enabled
579 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700580
581 Args:
582 target_zip: Zip file containing the source files to be included for OTA.
583 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800584 target_info: The BuildInfo instance that holds the target build info.
585 source_info: The BuildInfo instance that holds the source build info, if
586 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700587 """
588
Tao Baobcd1d162017-08-26 13:10:26 -0700589 def AddCompatibilityArchive(system_updated, vendor_updated):
590 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700591
Tao Baobcd1d162017-08-26 13:10:26 -0700592 Args:
593 system_updated: If True, the system image will be updated and therefore
594 its metadata should be included.
595 vendor_updated: If True, the vendor image will be updated and therefore
596 its metadata should be included.
597 """
598 # Determine what metadata we need. Files are names relative to META/.
599 compatibility_files = []
600 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
601 system_metadata = ("system_manifest.xml", "system_matrix.xml")
602 if vendor_updated:
603 compatibility_files += vendor_metadata
604 if system_updated:
605 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700606
Tao Baobcd1d162017-08-26 13:10:26 -0700607 # Create new archive.
608 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800609 compatibility_archive_zip = zipfile.ZipFile(
610 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700611
Tao Baobcd1d162017-08-26 13:10:26 -0700612 # Add metadata.
613 for file_name in compatibility_files:
614 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700615
Tao Baobcd1d162017-08-26 13:10:26 -0700616 if target_file_name in target_zip.namelist():
617 data = target_zip.read(target_file_name)
618 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700619
Tao Baobcd1d162017-08-26 13:10:26 -0700620 # Ensure files are written before we copy into output_zip.
621 compatibility_archive_zip.close()
622
623 # Only add the archive if we have any compatibility info.
624 if compatibility_archive_zip.namelist():
625 common.ZipWrite(output_zip, compatibility_archive.name,
626 arcname="compatibility.zip",
627 compress_type=zipfile.ZIP_STORED)
628
629 # Will only proceed if the target has enabled the Treble support (as well as
630 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800631 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700632 return
633
634 # We don't support OEM thumbprint in Treble world (which calculates
635 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800636 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700637
638 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800639 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700640 AddCompatibilityArchive(True, True)
641 return
642
Tao Bao481bab82017-12-21 11:23:09 -0800643 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700644
Tao Bao481bab82017-12-21 11:23:09 -0800645 source_fp = source_info.fingerprint
646 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700647 system_updated = source_fp != target_fp
648
Tao Bao481bab82017-12-21 11:23:09 -0800649 source_fp_vendor = source_info.GetVendorBuildProp(
650 "ro.vendor.build.fingerprint")
651 target_fp_vendor = target_info.GetVendorBuildProp(
652 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700653 vendor_updated = source_fp_vendor != target_fp_vendor
654
655 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700656
657
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700658def WriteFullOTAPackage(input_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800659 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700660
Tao Bao481bab82017-12-21 11:23:09 -0800661 # We don't know what version it will be installed on top of. We expect the API
662 # just won't change very often. Similarly for fstab, it might have changed in
663 # the target build.
664 target_api_version = target_info["recovery_api_version"]
665 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700666
Tao Bao481bab82017-12-21 11:23:09 -0800667 if target_info.oem_props and not OPTIONS.oem_no_mount:
668 target_info.WriteMountOemScript(script)
669
Tao Baodf3a48b2018-01-10 16:30:43 -0800670 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700671
Doug Zongker05d3dea2009-06-22 11:32:31 -0700672 device_specific = common.DeviceSpecificParams(
673 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800674 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700675 output_zip=output_zip,
676 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700677 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700678 metadata=metadata,
679 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700680
Tao Bao457cbf62017-03-06 09:56:01 -0800681 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800682
Tao Bao481bab82017-12-21 11:23:09 -0800683 # Assertions (e.g. downgrade check, device properties check).
684 ts = target_info.GetBuildProp("ro.build.date.utc")
685 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700686 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700687
Tao Bao481bab82017-12-21 11:23:09 -0800688 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700689 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800690
691 # Two-step package strategy (in chronological order, which is *not*
692 # the order in which the generated script has things):
693 #
694 # if stage is not "2/3" or "3/3":
695 # write recovery image to boot partition
696 # set stage to "2/3"
697 # reboot to boot partition and restart recovery
698 # else if stage is "2/3":
699 # write recovery image to recovery partition
700 # set stage to "3/3"
701 # reboot to recovery partition and restart recovery
702 # else:
703 # (stage must be "3/3")
704 # set stage to ""
705 # do normal full package installation:
706 # wipe and install system, boot image, etc.
707 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700708 # complete script normally
709 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800710
711 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
712 OPTIONS.input_tmp, "RECOVERY")
713 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800714 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800715 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800716 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800717 assert fs.fs_type.upper() == "EMMC", \
718 "two-step packages only supported on devices with EMMC /misc partitions"
719 bcb_dev = {"bcb_dev": fs.device}
720 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
721 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700722if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800723""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800724
725 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
726 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800727 script.WriteRawImage("/recovery", "recovery.img")
728 script.AppendExtra("""
729set_stage("%(bcb_dev)s", "3/3");
730reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700731else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800732""" % bcb_dev)
733
Tao Baod42e97e2016-11-30 12:11:57 -0800734 # Stage 3/3: Make changes.
735 script.Comment("Stage 3/3")
736
Tao Bao6c55a8a2015-04-08 15:30:27 -0700737 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800738 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700739
Doug Zongkere5ff5902012-01-17 10:55:37 -0800740 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700741
Doug Zongker01ce19c2014-02-04 13:48:15 -0800742 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700743
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700744 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800745 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700746 if HasVendorPartition(input_zip):
747 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700748
Doug Zongker4b9596f2014-06-09 14:15:45 -0700749 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800750
Tao Bao457cbf62017-03-06 09:56:01 -0800751 # Full OTA is done as an "incremental" against an empty source image. This
752 # has the effect of writing new data from the package to the entire
753 # partition, but lets us reuse the updater code that writes incrementals to
754 # do it.
Tao Baoc765cca2018-01-31 17:32:40 -0800755 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip)
Tao Bao457cbf62017-03-06 09:56:01 -0800756 system_tgt.ResetFileMap()
757 system_diff = common.BlockDifference("system", system_tgt, src=None)
758 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700759
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700760 boot_img = common.GetBootableImage(
761 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800762
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700763 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700764 script.ShowProgress(0.1, 0)
765
Tao Baoc765cca2018-01-31 17:32:40 -0800766 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip)
Tao Bao457cbf62017-03-06 09:56:01 -0800767 vendor_tgt.ResetFileMap()
768 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
769 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700770
Tao Bao481bab82017-12-21 11:23:09 -0800771 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700772
Tao Bao481bab82017-12-21 11:23:09 -0800773 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700774 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700775
Doug Zongker01ce19c2014-02-04 13:48:15 -0800776 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700777 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700778
Doug Zongker01ce19c2014-02-04 13:48:15 -0800779 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700780 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700781
Doug Zongker1c390a22009-05-14 19:06:36 -0700782 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700783 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700784
Doug Zongker14833602010-02-02 13:12:04 -0800785 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800786
Doug Zongker922206e2014-03-04 13:16:24 -0800787 if OPTIONS.wipe_user_data:
788 script.ShowProgress(0.1, 10)
789 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700790
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800791 if OPTIONS.two_step:
792 script.AppendExtra("""
793set_stage("%(bcb_dev)s", "");
794""" % bcb_dev)
795 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800796
797 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
798 script.Comment("Stage 1/3")
799 _WriteRecoveryImageToBoot(script, output_zip)
800
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800801 script.AppendExtra("""
802set_stage("%(bcb_dev)s", "2/3");
803reboot_now("%(bcb_dev)s", "");
804endif;
805endif;
806""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800807
Tao Bao5d182562016-02-23 11:38:39 -0800808 script.SetProgress(1)
809 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800810 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700811 WriteMetadata(metadata, output_zip)
812
Doug Zongkerfc44a512014-08-26 13:10:25 -0700813
Doug Zongker2ea21062010-04-28 16:05:21 -0700814def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800815 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
816 common.ZipWriteStr(output_zip, METADATA_NAME, value,
817 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700818
Doug Zongkerfc44a512014-08-26 13:10:25 -0700819
Tao Bao481bab82017-12-21 11:23:09 -0800820def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800821 # Only incremental OTAs are allowed to reach here.
822 assert OPTIONS.incremental_source is not None
823
Tao Bao481bab82017-12-21 11:23:09 -0800824 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
825 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800826 is_downgrade = long(post_timestamp) < long(pre_timestamp)
827
828 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800829 if not is_downgrade:
830 raise RuntimeError("--downgrade specified but no downgrade detected: "
831 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800832 metadata["ota-downgrade"] = "yes"
833 elif OPTIONS.timestamp:
834 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800835 raise RuntimeError("--override_timestamp specified but no timestamp hack "
836 "needed: pre: %s, post: %s" % (pre_timestamp,
837 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800838 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800839 else:
840 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800841 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800842 "pre: %s, post: %s. Need to specify "
843 "--override_timestamp OR --downgrade to allow "
844 "building the incremental." % (pre_timestamp,
845 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800846 metadata["post-timestamp"] = post_timestamp
847
848
Tao Baodf3a48b2018-01-10 16:30:43 -0800849def GetPackageMetadata(target_info, source_info=None):
850 """Generates and returns the metadata dict.
851
852 It generates a dict() that contains the info to be written into an OTA
853 package (META-INF/com/android/metadata). It also handles the detection of
854 downgrade / timestamp override / data wipe based on the global options.
855
856 Args:
857 target_info: The BuildInfo instance that holds the target build info.
858 source_info: The BuildInfo instance that holds the source build info, or
859 None if generating full OTA.
860
861 Returns:
862 A dict to be written into package metadata entry.
863 """
864 assert isinstance(target_info, BuildInfo)
865 assert source_info is None or isinstance(source_info, BuildInfo)
866
867 metadata = {
868 'post-build' : target_info.fingerprint,
869 'post-build-incremental' : target_info.GetBuildProp(
870 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800871 'post-sdk-level' : target_info.GetBuildProp(
872 'ro.build.version.sdk'),
873 'post-security-patch-level' : target_info.GetBuildProp(
874 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800875 }
876
877 if target_info.is_ab:
878 metadata['ota-type'] = 'AB'
879 metadata['ota-required-cache'] = '0'
880 else:
881 metadata['ota-type'] = 'BLOCK'
882
883 if OPTIONS.wipe_user_data:
884 metadata['ota-wipe'] = 'yes'
885
886 is_incremental = source_info is not None
887 if is_incremental:
888 metadata['pre-build'] = source_info.fingerprint
889 metadata['pre-build-incremental'] = source_info.GetBuildProp(
890 'ro.build.version.incremental')
891 metadata['pre-device'] = source_info.device
892 else:
893 metadata['pre-device'] = target_info.device
894
895 # Detect downgrades, or fill in the post-timestamp.
896 if is_incremental:
897 HandleDowngradeMetadata(metadata, target_info, source_info)
898 else:
899 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
900
901 return metadata
902
903
Geremy Condra36bd3652014-02-06 19:45:10 -0800904def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800905 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
906 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800907
Tao Bao481bab82017-12-21 11:23:09 -0800908 target_api_version = target_info["recovery_api_version"]
909 source_api_version = source_info["recovery_api_version"]
910 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700911 print("WARNING: generating edify script for a source that "
912 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800913
Tao Bao481bab82017-12-21 11:23:09 -0800914 script = edify_generator.EdifyGenerator(
915 source_api_version, target_info, fstab=source_info["fstab"])
916
917 if target_info.oem_props or source_info.oem_props:
918 if not OPTIONS.oem_no_mount:
919 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -0700920
Tao Baodf3a48b2018-01-10 16:30:43 -0800921 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -0800922
Geremy Condra36bd3652014-02-06 19:45:10 -0800923 device_specific = common.DeviceSpecificParams(
924 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800925 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800926 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800927 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800928 output_zip=output_zip,
929 script=script,
930 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -0800931 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800932
Geremy Condra36bd3652014-02-06 19:45:10 -0800933 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800934 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800935 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800936 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800937 updating_boot = (not OPTIONS.two_step and
938 (source_boot.data != target_boot.data))
939
Geremy Condra36bd3652014-02-06 19:45:10 -0800940 target_recovery = common.GetBootableImage(
941 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800942
Tao Baoc765cca2018-01-31 17:32:40 -0800943 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip)
944 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip)
Tao Baodd2a5892015-03-12 12:32:37 -0700945
Tao Bao0582cb62017-12-21 11:47:01 -0800946 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -0800947 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -0800948 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -0700949
Tao Baof8acad12016-07-07 09:09:58 -0700950 # Check the first block of the source system partition for remount R/W only
951 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -0800952 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700953 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700954 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
955 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
956 # b) the blocks listed in block map may not contain all the bytes for a given
957 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -0800958 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -0700959 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
960 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700961 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800962 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700963 version=blockimgdiff_version,
964 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700965
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700966 if HasVendorPartition(target_zip):
967 if not HasVendorPartition(source_zip):
968 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoc765cca2018-01-31 17:32:40 -0800969 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip)
970 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800971
972 # Check first block of vendor partition for remount R/W only if
973 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -0800974 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800975 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700976 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700977 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800978 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700979 version=blockimgdiff_version,
980 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700981 else:
982 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800983
Tao Baobcd1d162017-08-26 13:10:26 -0700984 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -0800985 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700986
Tao Bao481bab82017-12-21 11:23:09 -0800987 # Assertions (e.g. device properties check).
988 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -0800989 device_specific.IncrementalOTA_Assertions()
990
991 # Two-step incremental package strategy (in chronological order,
992 # which is *not* the order in which the generated script has
993 # things):
994 #
995 # if stage is not "2/3" or "3/3":
996 # do verification on current system
997 # write recovery image to boot partition
998 # set stage to "2/3"
999 # reboot to boot partition and restart recovery
1000 # else if stage is "2/3":
1001 # write recovery image to recovery partition
1002 # set stage to "3/3"
1003 # reboot to recovery partition and restart recovery
1004 # else:
1005 # (stage must be "3/3")
1006 # perform update:
1007 # patch system files, etc.
1008 # force full install of new boot image
1009 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001010 # complete script normally
1011 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001012
1013 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001014 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001015 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001016 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001017 assert fs.fs_type.upper() == "EMMC", \
1018 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001019 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001020 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1021 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001022if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001023""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001024
1025 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1026 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001027 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001028 script.WriteRawImage("/recovery", "recovery.img")
1029 script.AppendExtra("""
1030set_stage("%(bcb_dev)s", "3/3");
1031reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001032else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001033""" % bcb_dev)
1034
Tao Baod42e97e2016-11-30 12:11:57 -08001035 # Stage 1/3: (a) Verify the current system.
1036 script.Comment("Stage 1/3")
1037
Tao Bao6c55a8a2015-04-08 15:30:27 -07001038 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001039 script.Print("Source: {}".format(source_info.fingerprint))
1040 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001041
Geremy Condra36bd3652014-02-06 19:45:10 -08001042 script.Print("Verifying current system...")
1043
1044 device_specific.IncrementalOTA_VerifyBegin()
1045
Tao Bao481bab82017-12-21 11:23:09 -08001046 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001047
Tao Baod8d14be2016-02-04 14:26:02 -08001048 # Check the required cache size (i.e. stashed blocks).
1049 size = []
1050 if system_diff:
1051 size.append(system_diff.required_cache)
1052 if vendor_diff:
1053 size.append(vendor_diff.required_cache)
1054
Geremy Condra36bd3652014-02-06 19:45:10 -08001055 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001056 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001057 d = common.Difference(target_boot, source_boot)
1058 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001059 if d is None:
1060 include_full_boot = True
1061 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1062 else:
1063 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001064
Tao Bao89fbb0f2017-01-10 10:47:58 -08001065 print("boot target: %d source: %d diff: %d" % (
1066 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001067
Doug Zongkerf8340082014-08-05 10:39:37 -07001068 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001069
Doug Zongkerf8340082014-08-05 10:39:37 -07001070 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1071 (boot_type, boot_device,
1072 source_boot.size, source_boot.sha1,
1073 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001074 size.append(target_boot.size)
1075
1076 if size:
1077 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001078
1079 device_specific.IncrementalOTA_VerifyEnd()
1080
1081 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001082 # Stage 1/3: (b) Write recovery image to /boot.
1083 _WriteRecoveryImageToBoot(script, output_zip)
1084
Geremy Condra36bd3652014-02-06 19:45:10 -08001085 script.AppendExtra("""
1086set_stage("%(bcb_dev)s", "2/3");
1087reboot_now("%(bcb_dev)s", "");
1088else
1089""" % bcb_dev)
1090
Tao Baod42e97e2016-11-30 12:11:57 -08001091 # Stage 3/3: Make changes.
1092 script.Comment("Stage 3/3")
1093
Jesse Zhao75bcea02015-01-06 10:59:53 -08001094 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001095 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001096 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001097 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001098
Geremy Condra36bd3652014-02-06 19:45:10 -08001099 script.Comment("---- start making changes here ----")
1100
1101 device_specific.IncrementalOTA_InstallBegin()
1102
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001103 system_diff.WriteScript(script, output_zip,
1104 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001105
Doug Zongkerfc44a512014-08-26 13:10:25 -07001106 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001107 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001108
1109 if OPTIONS.two_step:
1110 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1111 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001112 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001113
1114 if not OPTIONS.two_step:
1115 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001116 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001117 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001118 script.Print("Installing boot image...")
1119 script.WriteRawImage("/boot", "boot.img")
1120 else:
1121 # Produce the boot image by applying a patch to the current
1122 # contents of the boot partition, and write it back to the
1123 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001124 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001125 script.Print("Patching boot image...")
1126 script.ShowProgress(0.1, 10)
1127 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1128 % (boot_type, boot_device,
1129 source_boot.size, source_boot.sha1,
1130 target_boot.size, target_boot.sha1),
1131 "-",
1132 target_boot.size, target_boot.sha1,
1133 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001134 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001135 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001136
1137 # Do device-specific installation (eg, write radio image).
1138 device_specific.IncrementalOTA_InstallEnd()
1139
1140 if OPTIONS.extra_script is not None:
1141 script.AppendExtra(OPTIONS.extra_script)
1142
Doug Zongker922206e2014-03-04 13:16:24 -08001143 if OPTIONS.wipe_user_data:
1144 script.Print("Erasing user data...")
1145 script.FormatPartition("/data")
1146
Geremy Condra36bd3652014-02-06 19:45:10 -08001147 if OPTIONS.two_step:
1148 script.AppendExtra("""
1149set_stage("%(bcb_dev)s", "");
1150endif;
1151endif;
1152""" % bcb_dev)
1153
1154 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001155 # For downgrade OTAs, we prefer to use the update-binary in the source
1156 # build that is actually newer than the one in the target build.
1157 if OPTIONS.downgrade:
1158 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1159 else:
1160 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001161 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001162 WriteMetadata(metadata, output_zip)
1163
Doug Zongker32b527d2014-03-04 10:03:02 -08001164
Tao Baoc098e9e2016-01-07 13:03:56 -08001165def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1166 source_file=None):
1167 """Generate an Android OTA package that has A/B update payload."""
1168
Tao Bao2dd1c482017-02-03 16:49:39 -08001169 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1170 expected_length=None):
1171 """Compute the streaming metadata for a given zip.
1172
1173 When 'reserve_space' is True, we reserve extra space for the offset and
1174 length of the metadata entry itself, although we don't know the final
1175 values until the package gets signed. This function will be called again
1176 after signing. We then write the actual values and pad the string to the
1177 length we set earlier. Note that we can't use the actual length of the
1178 metadata entry in the second run. Otherwise the offsets for other entries
1179 will be changing again.
1180 """
Tao Baoc96316c2017-01-24 22:10:49 -08001181
1182 def ComputeEntryOffsetSize(name):
1183 """Compute the zip entry offset and size."""
1184 info = zip_file.getinfo(name)
1185 offset = info.header_offset + len(info.FileHeader())
1186 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001187 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001188
1189 # payload.bin and payload_properties.txt must exist.
1190 offsets = [ComputeEntryOffsetSize('payload.bin'),
1191 ComputeEntryOffsetSize('payload_properties.txt')]
1192
1193 # care_map.txt is available only if dm-verity is enabled.
1194 if 'care_map.txt' in zip_file.namelist():
1195 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001196
Tao Bao21803d32017-04-19 10:16:09 -07001197 if 'compatibility.zip' in zip_file.namelist():
1198 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1199
Tao Bao2dd1c482017-02-03 16:49:39 -08001200 # 'META-INF/com/android/metadata' is required. We don't know its actual
1201 # offset and length (as well as the values for other entries). So we
1202 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1203 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1204 # beginning of the zip), as well as the possible value changes in other
1205 # entries.
1206 if reserve_space:
1207 offsets.append('metadata:' + ' ' * 10)
1208 else:
1209 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1210
1211 value = ','.join(offsets)
1212 if expected_length is not None:
1213 assert len(value) <= expected_length, \
1214 'Insufficient reserved space: reserved=%d, actual=%d' % (
1215 expected_length, len(value))
1216 value += ' ' * (expected_length - len(value))
1217 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001218
Tao Baodea0f8b2016-06-20 17:55:06 -07001219 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001220 temp_zip_file = tempfile.NamedTemporaryFile()
1221 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1222 compression=zipfile.ZIP_DEFLATED)
1223
Tao Bao481bab82017-12-21 11:23:09 -08001224 if source_file is not None:
1225 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1226 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1227 else:
1228 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1229 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001230
Tao Bao481bab82017-12-21 11:23:09 -08001231 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001232 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001233
Tao Baoc7b403a2018-01-30 18:19:04 -08001234 # Generate payload.
1235 payload = Payload()
1236 payload.Generate(target_file, source_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001237
Tao Baoc7b403a2018-01-30 18:19:04 -08001238 # Sign the payload.
1239 payload.Sign(PayloadSigner())
Tao Baoc098e9e2016-01-07 13:03:56 -08001240
Tao Baoc7b403a2018-01-30 18:19:04 -08001241 # Write the payload into output zip.
1242 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001243
Tianjie Xucfa86222016-03-07 16:31:19 -08001244 # If dm-verity is supported for the device, copy contents of care_map
1245 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001246 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001247 if (target_info.get("verity") == "true" or
1248 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001249 care_map_path = "META/care_map.txt"
1250 namelist = target_zip.namelist()
1251 if care_map_path in namelist:
1252 care_map_data = target_zip.read(care_map_path)
Tao Baoc7b403a2018-01-30 18:19:04 -08001253 # In order to support streaming, care_map.txt needs to be packed as
1254 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001255 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001256 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001257 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001258 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001259
Tao Bao481bab82017-12-21 11:23:09 -08001260 # source_info must be None for full OTAs.
Tao Baobcd1d162017-08-26 13:10:26 -07001261 if source_file is None:
Tao Bao481bab82017-12-21 11:23:09 -08001262 assert source_info is None
Tao Bao21803d32017-04-19 10:16:09 -07001263
Tao Baobcd1d162017-08-26 13:10:26 -07001264 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001265 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001266
Tao Bao21803d32017-04-19 10:16:09 -07001267 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001268
Tao Bao2dd1c482017-02-03 16:49:39 -08001269 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001270 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001271 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001272 WriteMetadata(metadata, output_zip)
1273 common.ZipClose(output_zip)
1274
Tao Bao2dd1c482017-02-03 16:49:39 -08001275 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
Tao Bao89d7ab22017-12-14 17:05:33 -08001276 # ZIP entries, as well as padding the entry headers. We do a preliminary
Tao Bao2dd1c482017-02-03 16:49:39 -08001277 # signing (with an incomplete metadata entry) to allow that to happen. Then
Tao Bao89d7ab22017-12-14 17:05:33 -08001278 # compute the ZIP entry offsets, write back the final metadata and do the
Tao Bao2dd1c482017-02-03 16:49:39 -08001279 # final signing.
Tao Bao89d7ab22017-12-14 17:05:33 -08001280 prelim_signing = common.MakeTempFile(suffix='.zip')
1281 SignOutput(temp_zip_file.name, prelim_signing)
Tao Bao2dd1c482017-02-03 16:49:39 -08001282 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001283
Tao Bao2dd1c482017-02-03 16:49:39 -08001284 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Bao89d7ab22017-12-14 17:05:33 -08001285 prelim_signing_zip = zipfile.ZipFile(prelim_signing, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001286 expected_length = len(metadata['ota-streaming-property-files'])
1287 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao89d7ab22017-12-14 17:05:33 -08001288 prelim_signing_zip, reserve_space=False, expected_length=expected_length)
1289 common.ZipClose(prelim_signing_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001290
Tao Bao89d7ab22017-12-14 17:05:33 -08001291 # Replace the METADATA entry.
1292 common.ZipDelete(prelim_signing, METADATA_NAME)
1293 output_zip = zipfile.ZipFile(prelim_signing, 'a',
Tao Bao2dd1c482017-02-03 16:49:39 -08001294 compression=zipfile.ZIP_DEFLATED)
Tao Bao2dd1c482017-02-03 16:49:39 -08001295 WriteMetadata(metadata, output_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001296 common.ZipClose(output_zip)
1297
1298 # Re-sign the package after updating the metadata entry.
Tao Bao89d7ab22017-12-14 17:05:33 -08001299 SignOutput(prelim_signing, output_file)
Tao Bao2dd1c482017-02-03 16:49:39 -08001300
1301 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao89d7ab22017-12-14 17:05:33 -08001302 output_zip = zipfile.ZipFile(output_file, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001303 actual = metadata['ota-streaming-property-files'].strip()
1304 expected = ComputeStreamingMetadata(output_zip)
1305 assert actual == expected, \
1306 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001307 common.ZipClose(output_zip)
1308
Tao Baoc098e9e2016-01-07 13:03:56 -08001309
Doug Zongkereef39442009-04-02 12:14:19 -07001310def main(argv):
1311
1312 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001313 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001314 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001315 elif o in ("-i", "--incremental_from"):
1316 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001317 elif o == "--full_radio":
1318 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001319 elif o == "--full_bootloader":
1320 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001321 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001322 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001323 elif o == "--downgrade":
1324 OPTIONS.downgrade = True
1325 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001326 elif o == "--override_timestamp":
1327 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001328 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001329 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001330 elif o == "--oem_no_mount":
1331 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001332 elif o in ("-e", "--extra_script"):
1333 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001334 elif o in ("-t", "--worker_threads"):
1335 if a.isdigit():
1336 OPTIONS.worker_threads = int(a)
1337 else:
1338 raise ValueError("Cannot parse value %r for option %r - only "
1339 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001340 elif o in ("-2", "--two_step"):
1341 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001342 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001343 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001344 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001345 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001346 elif o == "--block":
1347 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001348 elif o in ("-b", "--binary"):
1349 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001350 elif o == "--stash_threshold":
1351 try:
1352 OPTIONS.stash_threshold = float(a)
1353 except ValueError:
1354 raise ValueError("Cannot parse value %r for option %r - expecting "
1355 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001356 elif o == "--log_diff":
1357 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001358 elif o == "--payload_signer":
1359 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001360 elif o == "--payload_signer_args":
1361 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001362 elif o == "--extracted_input_target_files":
1363 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001364 else:
1365 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001366 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001367
1368 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001369 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001370 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001371 "package_key=",
1372 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001373 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001374 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001375 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001376 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001377 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001378 "extra_script=",
1379 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001380 "two_step",
1381 "no_signing",
1382 "block",
1383 "binary=",
1384 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001385 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001386 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001387 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001388 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001389 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001390 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001391 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001392 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001393
1394 if len(args) != 2:
1395 common.Usage(__doc__)
1396 sys.exit(1)
1397
Tao Bao5d182562016-02-23 11:38:39 -08001398 if OPTIONS.downgrade:
1399 # Sanity check to enforce a data wipe.
1400 if not OPTIONS.wipe_user_data:
1401 raise ValueError("Cannot downgrade without a data wipe")
1402
1403 # We should only allow downgrading incrementals (as opposed to full).
1404 # Otherwise the device may go back from arbitrary build with this full
1405 # OTA package.
1406 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001407 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001408
Tao Bao3e6161a2017-02-28 11:48:48 -08001409 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1410 "Cannot have --downgrade AND --override_timestamp both"
1411
Tao Bao2db13852018-01-08 22:28:57 -08001412 # Load the build info dicts from the zip directly or the extracted input
1413 # directory. We don't need to unzip the entire target-files zips, because they
1414 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1415 # When loading the info dicts, we don't need to provide the second parameter
1416 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1417 # some properties with their actual paths, such as 'selinux_fc',
1418 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001419 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001420 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001421 else:
Tao Bao2db13852018-01-08 22:28:57 -08001422 with zipfile.ZipFile(args[0], 'r') as input_zip:
1423 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001424
Tao Bao2db13852018-01-08 22:28:57 -08001425 if OPTIONS.verbose:
1426 print("--- target info ---")
1427 common.DumpInfoDict(OPTIONS.info_dict)
1428
1429 # Load the source build dict if applicable.
1430 if OPTIONS.incremental_source is not None:
1431 OPTIONS.target_info_dict = OPTIONS.info_dict
1432 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1433 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1434
1435 if OPTIONS.verbose:
1436 print("--- source info ---")
1437 common.DumpInfoDict(OPTIONS.source_info_dict)
1438
1439 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001440 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1441
Tao Baoc098e9e2016-01-07 13:03:56 -08001442 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1443
Christian Oderf63e2cd2017-05-01 22:30:15 +02001444 # Use the default key to sign the package if not specified with package_key.
1445 # package_keys are needed on ab_updates, so always define them if an
1446 # ab_update is getting created.
1447 if not OPTIONS.no_signing or ab_update:
1448 if OPTIONS.package_key is None:
1449 OPTIONS.package_key = OPTIONS.info_dict.get(
1450 "default_system_dev_certificate",
1451 "build/target/product/security/testkey")
1452 # Get signing keys
1453 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1454
Tao Baoc098e9e2016-01-07 13:03:56 -08001455 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001456 WriteABOTAPackageWithBrilloScript(
1457 target_file=args[0],
1458 output_file=args[1],
1459 source_file=OPTIONS.incremental_source)
1460
Tao Bao89fbb0f2017-01-10 10:47:58 -08001461 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001462 return
1463
Tao Bao2db13852018-01-08 22:28:57 -08001464 # Sanity check the loaded info dicts first.
1465 if OPTIONS.info_dict.get("no_recovery") == "true":
1466 raise common.ExternalError(
1467 "--- target build has specified no recovery ---")
1468
1469 # Non-A/B OTAs rely on /cache partition to store temporary files.
1470 cache_size = OPTIONS.info_dict.get("cache_size")
1471 if cache_size is None:
1472 print("--- can't determine the cache partition size ---")
1473 OPTIONS.cache_size = cache_size
1474
Doug Zongker1c390a22009-05-14 19:06:36 -07001475 if OPTIONS.extra_script is not None:
1476 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1477
Dan Willemsencea5cd22017-03-21 14:44:27 -07001478 if OPTIONS.extracted_input is not None:
1479 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001480 input_zip = zipfile.ZipFile(args[0], "r")
1481 else:
1482 print("unzipping target target-files...")
1483 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1484 args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001485 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001486
Tao Bao2db13852018-01-08 22:28:57 -08001487 # If the caller explicitly specified the device-specific extensions path via
1488 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1489 # is present in the target target_files. Otherwise, take the path of the file
1490 # from 'tool_extensions' in the info dict and look for that in the local
1491 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001492 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001493 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1494 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001495 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001496 OPTIONS.device_specific = from_input
1497 else:
Tao Bao2db13852018-01-08 22:28:57 -08001498 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001499
Doug Zongker37974732010-09-16 17:44:38 -07001500 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001501 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001502
Tao Bao767e3ac2015-11-10 12:19:19 -08001503 # Set up the output zip. Create a temporary zip file if signing is needed.
1504 if OPTIONS.no_signing:
1505 if os.path.exists(args[1]):
1506 os.unlink(args[1])
1507 output_zip = zipfile.ZipFile(args[1], "w",
1508 compression=zipfile.ZIP_DEFLATED)
1509 else:
1510 temp_zip_file = tempfile.NamedTemporaryFile()
1511 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1512 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001513
Tao Bao767e3ac2015-11-10 12:19:19 -08001514 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001515 if OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001516 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001517
Tao Bao32b80dc2018-01-08 22:50:47 -08001518 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001519 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001520 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001521 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001522 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001523 UNZIP_PATTERN)
Tao Bao32b80dc2018-01-08 22:50:47 -08001524
1525 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
1526
1527 if OPTIONS.log_diff:
1528 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001529 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001530 target_files_diff.recursiveDiff(
1531 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001532
Tao Bao2db13852018-01-08 22:28:57 -08001533 common.ZipClose(input_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001534 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001535
Tao Bao767e3ac2015-11-10 12:19:19 -08001536 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001537 if not OPTIONS.no_signing:
1538 SignOutput(temp_zip_file.name, args[1])
1539 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001540
Tao Bao89fbb0f2017-01-10 10:47:58 -08001541 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001542
1543
1544if __name__ == '__main__':
1545 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001546 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001547 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001548 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001549 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001550 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001551 finally:
1552 common.Cleanup()