blob: a22145a5660344cead25ca7ca38973067851496f [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
Tao Baof7140c02018-01-30 17:09:24 -080095 --include_secondary
96 Additionally include the payload for secondary slot images (default:
97 False). Only meaningful when generating A/B OTAs.
98
99 By default, an A/B OTA package doesn't contain the images for the
100 secondary slot (e.g. system_other.img). Specifying this flag allows
101 generating a separate payload that will install secondary slot images.
102
103 Such a package needs to be applied in a two-stage manner, with a reboot
104 in-between. During the first stage, the updater applies the primary
105 payload only. Upon finishing, it reboots the device into the newly updated
106 slot. It then continues to install the secondary payload to the inactive
107 slot, but without switching the active slot at the end (needs the matching
108 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
109
110 Due to the special install procedure, the secondary payload will be always
111 generated as a full payload.
112
Doug Zongker26e66192014-02-20 13:22:07 -0800113 --block
Tao Bao457cbf62017-03-06 09:56:01 -0800114 Generate a block-based OTA for non-A/B device. We have deprecated the
115 support for file-based OTA since O. Block-based OTA will be used by
116 default for all non-A/B devices. Keeping this flag here to not break
117 existing callers.
Doug Zongker26e66192014-02-20 13:22:07 -0800118
Doug Zongker25568482014-03-03 10:21:27 -0800119 -b (--binary) <file>
120 Use the given binary as the update-binary in the output package,
121 instead of the binary in the build's target_files. Use for
122 development only.
123
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200124 -t (--worker_threads) <int>
125 Specifies the number of worker-threads that will be used when
126 generating patches for incremental updates (defaults to 3).
127
Tao Bao8dcf7382015-05-21 14:09:49 -0700128 --stash_threshold <float>
129 Specifies the threshold that will be used to compute the maximum
130 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800131
Tao Baod62c6032015-11-30 09:40:20 -0800132 --log_diff <file>
133 Generate a log file that shows the differences in the source and target
134 builds for an incremental package. This option is only meaningful when
135 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700136
137 --payload_signer <signer>
138 Specify the signer when signing the payload and metadata for A/B OTAs.
139 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
140 with the package private key. If the private key cannot be accessed
141 directly, a payload signer that knows how to do that should be specified.
142 The signer will be supplied with "-inkey <path_to_key>",
143 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700144
145 --payload_signer_args <args>
146 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700147"""
148
Tao Bao89fbb0f2017-01-10 10:47:58 -0800149from __future__ import print_function
150
Doug Zongkerfc44a512014-08-26 13:10:25 -0700151import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800152import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700153import shlex
Tao Bao481bab82017-12-21 11:23:09 -0800154import subprocess
155import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700156import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700157import zipfile
158
159import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700160import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -0700161
Tao Bao481bab82017-12-21 11:23:09 -0800162if sys.hexversion < 0x02070000:
163 print("Python 2.7 or newer is required.", file=sys.stderr)
164 sys.exit(1)
165
166
Doug Zongkereef39442009-04-02 12:14:19 -0700167OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700168OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700169OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700170OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700171OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700172OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800173OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800174OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700175OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700176OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
177if OPTIONS.worker_threads == 0:
178 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800179OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800180OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900181OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800182OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800183OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700184OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800185OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700186OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700187OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700188# Stash size cannot exceed cache_size * threshold.
189OPTIONS.cache_size = None
190OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800191OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700192OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700193OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700194OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200195OPTIONS.key_passwords = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700196
Tao Bao2dd1c482017-02-03 16:49:39 -0800197METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800198UNZIP_PATTERN = ['IMAGES/*', 'META/*']
199
Tao Bao2dd1c482017-02-03 16:49:39 -0800200
Tao Bao481bab82017-12-21 11:23:09 -0800201class BuildInfo(object):
202 """A class that holds the information for a given build.
203
204 This class wraps up the property querying for a given source or target build.
205 It abstracts away the logic of handling OEM-specific properties, and caches
206 the commonly used properties such as fingerprint.
207
208 There are two types of info dicts: a) build-time info dict, which is generated
209 at build time (i.e. included in a target_files zip); b) OEM info dict that is
210 specified at package generation time (via command line argument
211 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
212 having "oem_fingerprint_properties" in build-time info dict), all the queries
213 would be answered based on build-time info dict only. Otherwise if using
214 OEM-specific properties, some of them will be calculated from two info dicts.
215
216 Users can query properties similarly as using a dict() (e.g. info['fstab']),
217 or to query build properties via GetBuildProp() or GetVendorBuildProp().
218
219 Attributes:
220 info_dict: The build-time info dict.
221 is_ab: Whether it's a build that uses A/B OTA.
222 oem_dicts: A list of OEM dicts.
223 oem_props: A list of OEM properties that should be read from OEM dicts; None
224 if the build doesn't use any OEM-specific property.
225 fingerprint: The fingerprint of the build, which would be calculated based
226 on OEM properties if applicable.
227 device: The device name, which could come from OEM dicts if applicable.
228 """
229
230 def __init__(self, info_dict, oem_dicts):
231 """Initializes a BuildInfo instance with the given dicts.
232
233 Arguments:
234 info_dict: The build-time info dict.
235 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
236 that it always uses the first dict to calculate the fingerprint or the
237 device name. The rest would be used for asserting OEM properties only
238 (e.g. one package can be installed on one of these devices).
239 """
240 self.info_dict = info_dict
241 self.oem_dicts = oem_dicts
242
243 self._is_ab = info_dict.get("ab_update") == "true"
244 self._oem_props = info_dict.get("oem_fingerprint_properties")
245
246 if self._oem_props:
247 assert oem_dicts, "OEM source required for this build"
248
249 # These two should be computed only after setting self._oem_props.
250 self._device = self.GetOemProperty("ro.product.device")
251 self._fingerprint = self.CalculateFingerprint()
252
253 @property
254 def is_ab(self):
255 return self._is_ab
256
257 @property
258 def device(self):
259 return self._device
260
261 @property
262 def fingerprint(self):
263 return self._fingerprint
264
265 @property
266 def oem_props(self):
267 return self._oem_props
268
269 def __getitem__(self, key):
270 return self.info_dict[key]
271
272 def get(self, key, default=None):
273 return self.info_dict.get(key, default)
274
275 def GetBuildProp(self, prop):
276 """Returns the inquired build property."""
277 try:
278 return self.info_dict.get("build.prop", {})[prop]
279 except KeyError:
280 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
281
282 def GetVendorBuildProp(self, prop):
283 """Returns the inquired vendor build property."""
284 try:
285 return self.info_dict.get("vendor.build.prop", {})[prop]
286 except KeyError:
287 raise common.ExternalError(
288 "couldn't find %s in vendor.build.prop" % (prop,))
289
290 def GetOemProperty(self, key):
291 if self.oem_props is not None and key in self.oem_props:
292 return self.oem_dicts[0][key]
293 return self.GetBuildProp(key)
294
295 def CalculateFingerprint(self):
296 if self.oem_props is None:
297 return self.GetBuildProp("ro.build.fingerprint")
298 return "%s/%s/%s:%s" % (
299 self.GetOemProperty("ro.product.brand"),
300 self.GetOemProperty("ro.product.name"),
301 self.GetOemProperty("ro.product.device"),
302 self.GetBuildProp("ro.build.thumbprint"))
303
304 def WriteMountOemScript(self, script):
305 assert self.oem_props is not None
306 recovery_mount_options = self.info_dict.get("recovery_mount_options")
307 script.Mount("/oem", recovery_mount_options)
308
309 def WriteDeviceAssertions(self, script, oem_no_mount):
310 # Read the property directly if not using OEM properties.
311 if not self.oem_props:
312 script.AssertDevice(self.device)
313 return
314
315 # Otherwise assert OEM properties.
316 if not self.oem_dicts:
317 raise common.ExternalError(
318 "No OEM file provided to answer expected assertions")
319
320 for prop in self.oem_props.split():
321 values = []
322 for oem_dict in self.oem_dicts:
323 if prop in oem_dict:
324 values.append(oem_dict[prop])
325 if not values:
326 raise common.ExternalError(
327 "The OEM file is missing the property %s" % (prop,))
328 script.AssertOemProperty(prop, values, oem_no_mount)
329
330
Tao Baofabe0832018-01-17 15:52:28 -0800331class PayloadSigner(object):
332 """A class that wraps the payload signing works.
333
334 When generating a Payload, hashes of the payload and metadata files will be
335 signed with the device key, either by calling an external payload signer or
336 by calling openssl with the package key. This class provides a unified
337 interface, so that callers can just call PayloadSigner.Sign().
338
339 If an external payload signer has been specified (OPTIONS.payload_signer), it
340 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
341 that the signing key should be provided as part of the payload_signer_args.
342 Otherwise without an external signer, it uses the package key
343 (OPTIONS.package_key) and calls openssl for the signing works.
344 """
345
346 def __init__(self):
347 if OPTIONS.payload_signer is None:
348 # Prepare the payload signing key.
349 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
350 pw = OPTIONS.key_passwords[OPTIONS.package_key]
351
352 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
353 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
354 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
355 cmd.extend(["-out", signing_key])
356
357 get_signing_key = common.Run(cmd, verbose=False, stdout=subprocess.PIPE,
358 stderr=subprocess.STDOUT)
359 stdoutdata, _ = get_signing_key.communicate()
360 assert get_signing_key.returncode == 0, \
361 "Failed to get signing key: {}".format(stdoutdata)
362
363 self.signer = "openssl"
364 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
365 "-pkeyopt", "digest:sha256"]
366 else:
367 self.signer = OPTIONS.payload_signer
368 self.signer_args = OPTIONS.payload_signer_args
369
370 def Sign(self, in_file):
371 """Signs the given input file. Returns the output filename."""
372 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
373 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
374 signing = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
375 stdoutdata, _ = signing.communicate()
376 assert signing.returncode == 0, \
377 "Failed to sign the input file: {}".format(stdoutdata)
378 return out_file
379
380
Tao Baoc7b403a2018-01-30 18:19:04 -0800381class Payload(object):
382 """Manages the creation and the signing of an A/B OTA Payload."""
383
384 PAYLOAD_BIN = 'payload.bin'
385 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800386 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
387 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Baoc7b403a2018-01-30 18:19:04 -0800388
Tao Bao667ff572018-02-10 00:02:40 -0800389 def __init__(self, secondary=False):
390 """Initializes a Payload instance.
391
392 Args:
393 secondary: Whether it's generating a secondary payload (default: False).
394 """
Tao Baoc7b403a2018-01-30 18:19:04 -0800395 # The place where the output from the subprocess should go.
396 self._log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
397 self.payload_file = None
398 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800399 self.secondary = secondary
Tao Baoc7b403a2018-01-30 18:19:04 -0800400
401 def Generate(self, target_file, source_file=None, additional_args=None):
402 """Generates a payload from the given target-files zip(s).
403
404 Args:
405 target_file: The filename of the target build target-files zip.
406 source_file: The filename of the source build target-files zip; or None if
407 generating a full OTA.
408 additional_args: A list of additional args that should be passed to
409 brillo_update_payload script; or None.
410 """
411 if additional_args is None:
412 additional_args = []
413
414 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
415 cmd = ["brillo_update_payload", "generate",
416 "--payload", payload_file,
417 "--target_image", target_file]
418 if source_file is not None:
419 cmd.extend(["--source_image", source_file])
420 cmd.extend(additional_args)
421 p = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
422 stdoutdata, _ = p.communicate()
423 assert p.returncode == 0, \
424 "brillo_update_payload generate failed: {}".format(stdoutdata)
425
426 self.payload_file = payload_file
427 self.payload_properties = None
428
429 def Sign(self, payload_signer):
430 """Generates and signs the hashes of the payload and metadata.
431
432 Args:
433 payload_signer: A PayloadSigner() instance that serves the signing work.
434
435 Raises:
436 AssertionError: On any failure when calling brillo_update_payload script.
437 """
438 assert isinstance(payload_signer, PayloadSigner)
439
440 # 1. Generate hashes of the payload and metadata files.
441 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
442 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
443 cmd = ["brillo_update_payload", "hash",
444 "--unsigned_payload", self.payload_file,
445 "--signature_size", "256",
446 "--metadata_hash_file", metadata_sig_file,
447 "--payload_hash_file", payload_sig_file]
448 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
449 p1.communicate()
450 assert p1.returncode == 0, "brillo_update_payload hash failed"
451
452 # 2. Sign the hashes.
453 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
454 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
455
456 # 3. Insert the signatures back into the payload file.
457 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
458 suffix=".bin")
459 cmd = ["brillo_update_payload", "sign",
460 "--unsigned_payload", self.payload_file,
461 "--payload", signed_payload_file,
462 "--signature_size", "256",
463 "--metadata_signature_file", signed_metadata_sig_file,
464 "--payload_signature_file", signed_payload_sig_file]
465 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
466 p1.communicate()
467 assert p1.returncode == 0, "brillo_update_payload sign failed"
468
469 # 4. Dump the signed payload properties.
470 properties_file = common.MakeTempFile(prefix="payload-properties-",
471 suffix=".txt")
472 cmd = ["brillo_update_payload", "properties",
473 "--payload", signed_payload_file,
474 "--properties_file", properties_file]
475 p1 = common.Run(cmd, stdout=self._log_file, stderr=subprocess.STDOUT)
476 p1.communicate()
477 assert p1.returncode == 0, "brillo_update_payload properties failed"
478
Tao Bao667ff572018-02-10 00:02:40 -0800479 if self.secondary:
480 with open(properties_file, "a") as f:
481 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
482
Tao Baoc7b403a2018-01-30 18:19:04 -0800483 if OPTIONS.wipe_user_data:
484 with open(properties_file, "a") as f:
485 f.write("POWERWASH=1\n")
486
487 self.payload_file = signed_payload_file
488 self.payload_properties = properties_file
489
Tao Bao667ff572018-02-10 00:02:40 -0800490 def WriteToZip(self, output_zip):
Tao Baoc7b403a2018-01-30 18:19:04 -0800491 """Writes the payload to the given zip.
492
493 Args:
494 output_zip: The output ZipFile instance.
495 """
496 assert self.payload_file is not None
497 assert self.payload_properties is not None
498
Tao Bao667ff572018-02-10 00:02:40 -0800499 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800500 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
501 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
502 else:
503 payload_arcname = Payload.PAYLOAD_BIN
504 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
505
Tao Baoc7b403a2018-01-30 18:19:04 -0800506 # Add the signed payload file and properties into the zip. In order to
507 # support streaming, we pack them as ZIP_STORED. So these entries can be
508 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800509 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Baoc7b403a2018-01-30 18:19:04 -0800510 compress_type=zipfile.ZIP_STORED)
511 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800512 arcname=payload_properties_arcname,
Tao Baoc7b403a2018-01-30 18:19:04 -0800513 compress_type=zipfile.ZIP_STORED)
514
515
Doug Zongkereef39442009-04-02 12:14:19 -0700516def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200517 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700518
Doug Zongker951495f2009-08-14 12:44:19 -0700519 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
520 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700521
522
Tao Bao481bab82017-12-21 11:23:09 -0800523def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800524 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800525 if not oem_source:
526 return None
527
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800528 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800529 for oem_file in oem_source:
530 with open(oem_file) as fp:
531 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800532 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700533
Doug Zongkereef39442009-04-02 12:14:19 -0700534
Tao Baod42e97e2016-11-30 12:11:57 -0800535def _WriteRecoveryImageToBoot(script, output_zip):
536 """Find and write recovery image to /boot in two-step OTA.
537
538 In two-step OTAs, we write recovery image to /boot as the first step so that
539 we can reboot to there and install a new recovery image to /recovery.
540 A special "recovery-two-step.img" will be preferred, which encodes the correct
541 path of "/boot". Otherwise the device may show "device is corrupt" message
542 when booting into /boot.
543
544 Fall back to using the regular recovery.img if the two-step recovery image
545 doesn't exist. Note that rebuilding the special image at this point may be
546 infeasible, because we don't have the desired boot signer and keys when
547 calling ota_from_target_files.py.
548 """
549
550 recovery_two_step_img_name = "recovery-two-step.img"
551 recovery_two_step_img_path = os.path.join(
552 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
553 if os.path.exists(recovery_two_step_img_path):
554 recovery_two_step_img = common.GetBootableImage(
555 recovery_two_step_img_name, recovery_two_step_img_name,
556 OPTIONS.input_tmp, "RECOVERY")
557 common.ZipWriteStr(
558 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800559 print("two-step package: using %s in stage 1/3" % (
560 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800561 script.WriteRawImage("/boot", recovery_two_step_img_name)
562 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800563 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800564 # The "recovery.img" entry has been written into package earlier.
565 script.WriteRawImage("/boot", "recovery.img")
566
567
Doug Zongkerc9253822014-02-04 12:17:58 -0800568def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700569 namelist = [name for name in target_files_zip.namelist()]
570 return ("SYSTEM/recovery-from-boot.p" in namelist or
571 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700572
Tao Bao457cbf62017-03-06 09:56:01 -0800573
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700574def HasVendorPartition(target_files_zip):
575 try:
576 target_files_zip.getinfo("VENDOR/")
577 return True
578 except KeyError:
579 return False
580
Tao Bao457cbf62017-03-06 09:56:01 -0800581
Tao Bao481bab82017-12-21 11:23:09 -0800582def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700583 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800584 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700585
586
Tao Bao481bab82017-12-21 11:23:09 -0800587def WriteFingerprintAssertion(script, target_info, source_info):
588 source_oem_props = source_info.oem_props
589 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700590
Tao Bao481bab82017-12-21 11:23:09 -0800591 if source_oem_props is None and target_oem_props is None:
592 script.AssertSomeFingerprint(
593 source_info.fingerprint, target_info.fingerprint)
594 elif source_oem_props is not None and target_oem_props is not None:
595 script.AssertSomeThumbprint(
596 target_info.GetBuildProp("ro.build.thumbprint"),
597 source_info.GetBuildProp("ro.build.thumbprint"))
598 elif source_oem_props is None and target_oem_props is not None:
599 script.AssertFingerprintOrThumbprint(
600 source_info.fingerprint,
601 target_info.GetBuildProp("ro.build.thumbprint"))
602 else:
603 script.AssertFingerprintOrThumbprint(
604 target_info.fingerprint,
605 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700606
Doug Zongkerfc44a512014-08-26 13:10:25 -0700607
Tao Bao481bab82017-12-21 11:23:09 -0800608def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
609 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700610 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700611
612 Metadata used for on-device compatibility verification is retrieved from
613 target_zip then added to compatibility.zip which is added to the output_zip
614 archive.
615
Tao Baobcd1d162017-08-26 13:10:26 -0700616 Compatibility archive should only be included for devices that have enabled
617 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700618
619 Args:
620 target_zip: Zip file containing the source files to be included for OTA.
621 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800622 target_info: The BuildInfo instance that holds the target build info.
623 source_info: The BuildInfo instance that holds the source build info, if
624 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700625 """
626
Tao Baobcd1d162017-08-26 13:10:26 -0700627 def AddCompatibilityArchive(system_updated, vendor_updated):
628 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700629
Tao Baobcd1d162017-08-26 13:10:26 -0700630 Args:
631 system_updated: If True, the system image will be updated and therefore
632 its metadata should be included.
633 vendor_updated: If True, the vendor image will be updated and therefore
634 its metadata should be included.
635 """
636 # Determine what metadata we need. Files are names relative to META/.
637 compatibility_files = []
638 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
639 system_metadata = ("system_manifest.xml", "system_matrix.xml")
640 if vendor_updated:
641 compatibility_files += vendor_metadata
642 if system_updated:
643 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700644
Tao Baobcd1d162017-08-26 13:10:26 -0700645 # Create new archive.
646 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800647 compatibility_archive_zip = zipfile.ZipFile(
648 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700649
Tao Baobcd1d162017-08-26 13:10:26 -0700650 # Add metadata.
651 for file_name in compatibility_files:
652 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700653
Tao Baobcd1d162017-08-26 13:10:26 -0700654 if target_file_name in target_zip.namelist():
655 data = target_zip.read(target_file_name)
656 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700657
Tao Baobcd1d162017-08-26 13:10:26 -0700658 # Ensure files are written before we copy into output_zip.
659 compatibility_archive_zip.close()
660
661 # Only add the archive if we have any compatibility info.
662 if compatibility_archive_zip.namelist():
663 common.ZipWrite(output_zip, compatibility_archive.name,
664 arcname="compatibility.zip",
665 compress_type=zipfile.ZIP_STORED)
666
667 # Will only proceed if the target has enabled the Treble support (as well as
668 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800669 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700670 return
671
672 # We don't support OEM thumbprint in Treble world (which calculates
673 # fingerprints in a different way as shown in CalculateFingerprint()).
Tao Bao481bab82017-12-21 11:23:09 -0800674 assert not target_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700675
676 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800677 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700678 AddCompatibilityArchive(True, True)
679 return
680
Tao Bao481bab82017-12-21 11:23:09 -0800681 assert not source_info.oem_props
Tao Baobcd1d162017-08-26 13:10:26 -0700682
Tao Bao481bab82017-12-21 11:23:09 -0800683 source_fp = source_info.fingerprint
684 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700685 system_updated = source_fp != target_fp
686
Tao Bao481bab82017-12-21 11:23:09 -0800687 source_fp_vendor = source_info.GetVendorBuildProp(
688 "ro.vendor.build.fingerprint")
689 target_fp_vendor = target_info.GetVendorBuildProp(
690 "ro.vendor.build.fingerprint")
Tao Baobcd1d162017-08-26 13:10:26 -0700691 vendor_updated = source_fp_vendor != target_fp_vendor
692
693 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700694
695
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700696def WriteFullOTAPackage(input_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800697 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700698
Tao Bao481bab82017-12-21 11:23:09 -0800699 # We don't know what version it will be installed on top of. We expect the API
700 # just won't change very often. Similarly for fstab, it might have changed in
701 # the target build.
702 target_api_version = target_info["recovery_api_version"]
703 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700704
Tao Bao481bab82017-12-21 11:23:09 -0800705 if target_info.oem_props and not OPTIONS.oem_no_mount:
706 target_info.WriteMountOemScript(script)
707
Tao Baodf3a48b2018-01-10 16:30:43 -0800708 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700709
Doug Zongker05d3dea2009-06-22 11:32:31 -0700710 device_specific = common.DeviceSpecificParams(
711 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800712 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700713 output_zip=output_zip,
714 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700715 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700716 metadata=metadata,
717 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700718
Tao Bao457cbf62017-03-06 09:56:01 -0800719 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800720
Tao Bao481bab82017-12-21 11:23:09 -0800721 # Assertions (e.g. downgrade check, device properties check).
722 ts = target_info.GetBuildProp("ro.build.date.utc")
723 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700724 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700725
Tao Bao481bab82017-12-21 11:23:09 -0800726 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700727 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800728
729 # Two-step package strategy (in chronological order, which is *not*
730 # the order in which the generated script has things):
731 #
732 # if stage is not "2/3" or "3/3":
733 # write recovery image to boot partition
734 # set stage to "2/3"
735 # reboot to boot partition and restart recovery
736 # else if stage is "2/3":
737 # write recovery image to recovery partition
738 # set stage to "3/3"
739 # reboot to recovery partition and restart recovery
740 # else:
741 # (stage must be "3/3")
742 # set stage to ""
743 # do normal full package installation:
744 # wipe and install system, boot image, etc.
745 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700746 # complete script normally
747 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800748
749 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
750 OPTIONS.input_tmp, "RECOVERY")
751 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800752 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800753 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800754 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800755 assert fs.fs_type.upper() == "EMMC", \
756 "two-step packages only supported on devices with EMMC /misc partitions"
757 bcb_dev = {"bcb_dev": fs.device}
758 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
759 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700760if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800761""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800762
763 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
764 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800765 script.WriteRawImage("/recovery", "recovery.img")
766 script.AppendExtra("""
767set_stage("%(bcb_dev)s", "3/3");
768reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700769else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800770""" % bcb_dev)
771
Tao Baod42e97e2016-11-30 12:11:57 -0800772 # Stage 3/3: Make changes.
773 script.Comment("Stage 3/3")
774
Tao Bao6c55a8a2015-04-08 15:30:27 -0700775 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800776 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700777
Doug Zongkere5ff5902012-01-17 10:55:37 -0800778 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700779
Doug Zongker01ce19c2014-02-04 13:48:15 -0800780 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700781
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700782 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800783 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700784 if HasVendorPartition(input_zip):
785 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700786
Doug Zongker4b9596f2014-06-09 14:15:45 -0700787 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800788
Tao Baoe709b092018-02-07 12:40:00 -0800789 # See the notes in WriteBlockIncrementalOTAPackage().
790 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
791
Tao Bao457cbf62017-03-06 09:56:01 -0800792 # Full OTA is done as an "incremental" against an empty source image. This
793 # has the effect of writing new data from the package to the entire
794 # partition, but lets us reuse the updater code that writes incrementals to
795 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800796 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
797 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800798 system_tgt.ResetFileMap()
799 system_diff = common.BlockDifference("system", system_tgt, src=None)
800 system_diff.WriteScript(script, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700801
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700802 boot_img = common.GetBootableImage(
803 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800804
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700805 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700806 script.ShowProgress(0.1, 0)
807
Tao Baoe709b092018-02-07 12:40:00 -0800808 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
809 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800810 vendor_tgt.ResetFileMap()
811 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
812 vendor_diff.WriteScript(script, output_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700813
Tao Bao481bab82017-12-21 11:23:09 -0800814 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700815
Tao Bao481bab82017-12-21 11:23:09 -0800816 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700817 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700818
Doug Zongker01ce19c2014-02-04 13:48:15 -0800819 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700820 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700821
Doug Zongker01ce19c2014-02-04 13:48:15 -0800822 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700823 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700824
Doug Zongker1c390a22009-05-14 19:06:36 -0700825 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700826 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700827
Doug Zongker14833602010-02-02 13:12:04 -0800828 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800829
Doug Zongker922206e2014-03-04 13:16:24 -0800830 if OPTIONS.wipe_user_data:
831 script.ShowProgress(0.1, 10)
832 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700833
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800834 if OPTIONS.two_step:
835 script.AppendExtra("""
836set_stage("%(bcb_dev)s", "");
837""" % bcb_dev)
838 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800839
840 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
841 script.Comment("Stage 1/3")
842 _WriteRecoveryImageToBoot(script, output_zip)
843
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800844 script.AppendExtra("""
845set_stage("%(bcb_dev)s", "2/3");
846reboot_now("%(bcb_dev)s", "");
847endif;
848endif;
849""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800850
Tao Bao5d182562016-02-23 11:38:39 -0800851 script.SetProgress(1)
852 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800853 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700854 WriteMetadata(metadata, output_zip)
855
Doug Zongkerfc44a512014-08-26 13:10:25 -0700856
Doug Zongker2ea21062010-04-28 16:05:21 -0700857def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800858 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
859 common.ZipWriteStr(output_zip, METADATA_NAME, value,
860 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700861
Doug Zongkerfc44a512014-08-26 13:10:25 -0700862
Tao Bao481bab82017-12-21 11:23:09 -0800863def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800864 # Only incremental OTAs are allowed to reach here.
865 assert OPTIONS.incremental_source is not None
866
Tao Bao481bab82017-12-21 11:23:09 -0800867 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
868 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800869 is_downgrade = long(post_timestamp) < long(pre_timestamp)
870
871 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800872 if not is_downgrade:
873 raise RuntimeError("--downgrade specified but no downgrade detected: "
874 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800875 metadata["ota-downgrade"] = "yes"
876 elif OPTIONS.timestamp:
877 if not is_downgrade:
Tao Bao481bab82017-12-21 11:23:09 -0800878 raise RuntimeError("--override_timestamp specified but no timestamp hack "
879 "needed: pre: %s, post: %s" % (pre_timestamp,
880 post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800881 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800882 else:
883 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800884 raise RuntimeError("Downgrade detected based on timestamp check: "
Tao Bao481bab82017-12-21 11:23:09 -0800885 "pre: %s, post: %s. Need to specify "
886 "--override_timestamp OR --downgrade to allow "
887 "building the incremental." % (pre_timestamp,
888 post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800889 metadata["post-timestamp"] = post_timestamp
890
891
Tao Baodf3a48b2018-01-10 16:30:43 -0800892def GetPackageMetadata(target_info, source_info=None):
893 """Generates and returns the metadata dict.
894
895 It generates a dict() that contains the info to be written into an OTA
896 package (META-INF/com/android/metadata). It also handles the detection of
897 downgrade / timestamp override / data wipe based on the global options.
898
899 Args:
900 target_info: The BuildInfo instance that holds the target build info.
901 source_info: The BuildInfo instance that holds the source build info, or
902 None if generating full OTA.
903
904 Returns:
905 A dict to be written into package metadata entry.
906 """
907 assert isinstance(target_info, BuildInfo)
908 assert source_info is None or isinstance(source_info, BuildInfo)
909
910 metadata = {
911 'post-build' : target_info.fingerprint,
912 'post-build-incremental' : target_info.GetBuildProp(
913 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800914 'post-sdk-level' : target_info.GetBuildProp(
915 'ro.build.version.sdk'),
916 'post-security-patch-level' : target_info.GetBuildProp(
917 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800918 }
919
920 if target_info.is_ab:
921 metadata['ota-type'] = 'AB'
922 metadata['ota-required-cache'] = '0'
923 else:
924 metadata['ota-type'] = 'BLOCK'
925
926 if OPTIONS.wipe_user_data:
927 metadata['ota-wipe'] = 'yes'
928
929 is_incremental = source_info is not None
930 if is_incremental:
931 metadata['pre-build'] = source_info.fingerprint
932 metadata['pre-build-incremental'] = source_info.GetBuildProp(
933 'ro.build.version.incremental')
934 metadata['pre-device'] = source_info.device
935 else:
936 metadata['pre-device'] = target_info.device
937
938 # Detect downgrades, or fill in the post-timestamp.
939 if is_incremental:
940 HandleDowngradeMetadata(metadata, target_info, source_info)
941 else:
942 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
943
944 return metadata
945
946
Geremy Condra36bd3652014-02-06 19:45:10 -0800947def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao481bab82017-12-21 11:23:09 -0800948 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
949 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800950
Tao Bao481bab82017-12-21 11:23:09 -0800951 target_api_version = target_info["recovery_api_version"]
952 source_api_version = source_info["recovery_api_version"]
953 if source_api_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700954 print("WARNING: generating edify script for a source that "
955 "can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -0800956
Tao Bao481bab82017-12-21 11:23:09 -0800957 script = edify_generator.EdifyGenerator(
958 source_api_version, target_info, fstab=source_info["fstab"])
959
960 if target_info.oem_props or source_info.oem_props:
961 if not OPTIONS.oem_no_mount:
962 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -0700963
Tao Baodf3a48b2018-01-10 16:30:43 -0800964 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -0800965
Geremy Condra36bd3652014-02-06 19:45:10 -0800966 device_specific = common.DeviceSpecificParams(
967 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800968 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800969 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800970 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -0800971 output_zip=output_zip,
972 script=script,
973 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -0800974 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800975
Geremy Condra36bd3652014-02-06 19:45:10 -0800976 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800977 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800978 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -0800979 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -0800980 updating_boot = (not OPTIONS.two_step and
981 (source_boot.data != target_boot.data))
982
Geremy Condra36bd3652014-02-06 19:45:10 -0800983 target_recovery = common.GetBootableImage(
984 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800985
Tao Baoe709b092018-02-07 12:40:00 -0800986 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
987 # shared blocks (i.e. some blocks will show up in multiple files' block
988 # list). We can only allocate such shared blocks to the first "owner", and
989 # disable imgdiff for all later occurrences.
990 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
991 target_info.get('ext4_share_dup_blocks') == "true")
992 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
993 allow_shared_blocks)
994 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
995 allow_shared_blocks)
Tao Baodd2a5892015-03-12 12:32:37 -0700996
Tao Bao0582cb62017-12-21 11:47:01 -0800997 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -0800998 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -0800999 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001000
Tao Baof8acad12016-07-07 09:09:58 -07001001 # Check the first block of the source system partition for remount R/W only
1002 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001003 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001004 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001005 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1006 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1007 # b) the blocks listed in block map may not contain all the bytes for a given
1008 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001009 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001010 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1011 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001012 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001013 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001014 version=blockimgdiff_version,
1015 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001016
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001017 if HasVendorPartition(target_zip):
1018 if not HasVendorPartition(source_zip):
1019 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001020 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1021 allow_shared_blocks)
1022 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.target_tmp, target_zip,
1023 allow_shared_blocks)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001024
1025 # Check first block of vendor partition for remount R/W only if
1026 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001027 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001028 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001029 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001030 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001031 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001032 version=blockimgdiff_version,
1033 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001034 else:
1035 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001036
Tao Baobcd1d162017-08-26 13:10:26 -07001037 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001038 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001039
Tao Bao481bab82017-12-21 11:23:09 -08001040 # Assertions (e.g. device properties check).
1041 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001042 device_specific.IncrementalOTA_Assertions()
1043
1044 # Two-step incremental package strategy (in chronological order,
1045 # which is *not* the order in which the generated script has
1046 # things):
1047 #
1048 # if stage is not "2/3" or "3/3":
1049 # do verification on current system
1050 # write recovery image to boot partition
1051 # set stage to "2/3"
1052 # reboot to boot partition and restart recovery
1053 # else if stage is "2/3":
1054 # write recovery image to recovery partition
1055 # set stage to "3/3"
1056 # reboot to recovery partition and restart recovery
1057 # else:
1058 # (stage must be "3/3")
1059 # perform update:
1060 # patch system files, etc.
1061 # force full install of new boot image
1062 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001063 # complete script normally
1064 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001065
1066 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001067 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001068 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001069 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001070 assert fs.fs_type.upper() == "EMMC", \
1071 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001072 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001073 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1074 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001075if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001076""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001077
1078 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1079 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001080 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001081 script.WriteRawImage("/recovery", "recovery.img")
1082 script.AppendExtra("""
1083set_stage("%(bcb_dev)s", "3/3");
1084reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001085else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001086""" % bcb_dev)
1087
Tao Baod42e97e2016-11-30 12:11:57 -08001088 # Stage 1/3: (a) Verify the current system.
1089 script.Comment("Stage 1/3")
1090
Tao Bao6c55a8a2015-04-08 15:30:27 -07001091 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001092 script.Print("Source: {}".format(source_info.fingerprint))
1093 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001094
Geremy Condra36bd3652014-02-06 19:45:10 -08001095 script.Print("Verifying current system...")
1096
1097 device_specific.IncrementalOTA_VerifyBegin()
1098
Tao Bao481bab82017-12-21 11:23:09 -08001099 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001100
Tao Baod8d14be2016-02-04 14:26:02 -08001101 # Check the required cache size (i.e. stashed blocks).
1102 size = []
1103 if system_diff:
1104 size.append(system_diff.required_cache)
1105 if vendor_diff:
1106 size.append(vendor_diff.required_cache)
1107
Geremy Condra36bd3652014-02-06 19:45:10 -08001108 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001109 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001110 d = common.Difference(target_boot, source_boot)
1111 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001112 if d is None:
1113 include_full_boot = True
1114 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1115 else:
1116 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001117
Tao Bao89fbb0f2017-01-10 10:47:58 -08001118 print("boot target: %d source: %d diff: %d" % (
1119 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001120
Doug Zongkerf8340082014-08-05 10:39:37 -07001121 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001122
Doug Zongkerf8340082014-08-05 10:39:37 -07001123 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1124 (boot_type, boot_device,
1125 source_boot.size, source_boot.sha1,
1126 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001127 size.append(target_boot.size)
1128
1129 if size:
1130 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001131
1132 device_specific.IncrementalOTA_VerifyEnd()
1133
1134 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001135 # Stage 1/3: (b) Write recovery image to /boot.
1136 _WriteRecoveryImageToBoot(script, output_zip)
1137
Geremy Condra36bd3652014-02-06 19:45:10 -08001138 script.AppendExtra("""
1139set_stage("%(bcb_dev)s", "2/3");
1140reboot_now("%(bcb_dev)s", "");
1141else
1142""" % bcb_dev)
1143
Tao Baod42e97e2016-11-30 12:11:57 -08001144 # Stage 3/3: Make changes.
1145 script.Comment("Stage 3/3")
1146
Jesse Zhao75bcea02015-01-06 10:59:53 -08001147 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001148 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001149 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001150 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001151
Geremy Condra36bd3652014-02-06 19:45:10 -08001152 script.Comment("---- start making changes here ----")
1153
1154 device_specific.IncrementalOTA_InstallBegin()
1155
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001156 system_diff.WriteScript(script, output_zip,
1157 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001158
Doug Zongkerfc44a512014-08-26 13:10:25 -07001159 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001160 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001161
1162 if OPTIONS.two_step:
1163 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1164 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001165 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001166
1167 if not OPTIONS.two_step:
1168 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001169 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001170 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001171 script.Print("Installing boot image...")
1172 script.WriteRawImage("/boot", "boot.img")
1173 else:
1174 # Produce the boot image by applying a patch to the current
1175 # contents of the boot partition, and write it back to the
1176 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001177 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001178 script.Print("Patching boot image...")
1179 script.ShowProgress(0.1, 10)
1180 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1181 % (boot_type, boot_device,
1182 source_boot.size, source_boot.sha1,
1183 target_boot.size, target_boot.sha1),
1184 "-",
1185 target_boot.size, target_boot.sha1,
1186 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001187 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001188 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001189
1190 # Do device-specific installation (eg, write radio image).
1191 device_specific.IncrementalOTA_InstallEnd()
1192
1193 if OPTIONS.extra_script is not None:
1194 script.AppendExtra(OPTIONS.extra_script)
1195
Doug Zongker922206e2014-03-04 13:16:24 -08001196 if OPTIONS.wipe_user_data:
1197 script.Print("Erasing user data...")
1198 script.FormatPartition("/data")
1199
Geremy Condra36bd3652014-02-06 19:45:10 -08001200 if OPTIONS.two_step:
1201 script.AppendExtra("""
1202set_stage("%(bcb_dev)s", "");
1203endif;
1204endif;
1205""" % bcb_dev)
1206
1207 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001208 # For downgrade OTAs, we prefer to use the update-binary in the source
1209 # build that is actually newer than the one in the target build.
1210 if OPTIONS.downgrade:
1211 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1212 else:
1213 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001214 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001215 WriteMetadata(metadata, output_zip)
1216
Doug Zongker32b527d2014-03-04 10:03:02 -08001217
Tao Baof7140c02018-01-30 17:09:24 -08001218def GetTargetFilesZipForSecondaryImages(input_file):
1219 """Returns a target-files.zip file for generating secondary payload.
1220
1221 Although the original target-files.zip already contains secondary slot
1222 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1223 ones without _other suffix. Note that we cannot instead modify the names in
1224 META/ab_partitions.txt, because there are no matching partitions on device.
1225
1226 For the partitions that don't have secondary images, the ones for primary
1227 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1228 bootloader images in the inactive slot.
1229
1230 Args:
1231 input_file: The input target-files.zip file.
1232
1233 Returns:
1234 The filename of the target-files.zip for generating secondary payload.
1235 """
1236 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1237 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1238
1239 input_tmp, input_zip = common.UnzipTemp(input_file, UNZIP_PATTERN)
1240 for info in input_zip.infolist():
1241 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1242 if info.filename == 'IMAGES/system_other.img':
1243 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1244
1245 # Primary images and friends need to be skipped explicitly.
1246 elif info.filename in ('IMAGES/system.img',
1247 'IMAGES/system.map'):
1248 pass
1249
1250 elif info.filename.startswith(('META/', 'IMAGES/')):
1251 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1252
1253 common.ZipClose(input_zip)
1254 common.ZipClose(target_zip)
1255
1256 return target_file
1257
1258
Tao Baoc098e9e2016-01-07 13:03:56 -08001259def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1260 source_file=None):
1261 """Generate an Android OTA package that has A/B update payload."""
1262
Tao Bao2dd1c482017-02-03 16:49:39 -08001263 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1264 expected_length=None):
1265 """Compute the streaming metadata for a given zip.
1266
1267 When 'reserve_space' is True, we reserve extra space for the offset and
1268 length of the metadata entry itself, although we don't know the final
1269 values until the package gets signed. This function will be called again
1270 after signing. We then write the actual values and pad the string to the
1271 length we set earlier. Note that we can't use the actual length of the
1272 metadata entry in the second run. Otherwise the offsets for other entries
1273 will be changing again.
1274 """
Tao Baoc96316c2017-01-24 22:10:49 -08001275
1276 def ComputeEntryOffsetSize(name):
1277 """Compute the zip entry offset and size."""
1278 info = zip_file.getinfo(name)
1279 offset = info.header_offset + len(info.FileHeader())
1280 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001281 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001282
1283 # payload.bin and payload_properties.txt must exist.
1284 offsets = [ComputeEntryOffsetSize('payload.bin'),
1285 ComputeEntryOffsetSize('payload_properties.txt')]
1286
1287 # care_map.txt is available only if dm-verity is enabled.
1288 if 'care_map.txt' in zip_file.namelist():
1289 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001290
Tao Bao21803d32017-04-19 10:16:09 -07001291 if 'compatibility.zip' in zip_file.namelist():
1292 offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
1293
Tao Bao2dd1c482017-02-03 16:49:39 -08001294 # 'META-INF/com/android/metadata' is required. We don't know its actual
1295 # offset and length (as well as the values for other entries). So we
1296 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1297 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1298 # beginning of the zip), as well as the possible value changes in other
1299 # entries.
1300 if reserve_space:
1301 offsets.append('metadata:' + ' ' * 10)
1302 else:
1303 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1304
1305 value = ','.join(offsets)
1306 if expected_length is not None:
1307 assert len(value) <= expected_length, \
1308 'Insufficient reserved space: reserved=%d, actual=%d' % (
1309 expected_length, len(value))
1310 value += ' ' * (expected_length - len(value))
1311 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001312
Tao Baodea0f8b2016-06-20 17:55:06 -07001313 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001314 temp_zip_file = tempfile.NamedTemporaryFile()
1315 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1316 compression=zipfile.ZIP_DEFLATED)
1317
Tao Bao481bab82017-12-21 11:23:09 -08001318 if source_file is not None:
1319 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1320 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1321 else:
1322 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1323 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001324
Tao Bao481bab82017-12-21 11:23:09 -08001325 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001326 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001327
Tao Baoc7b403a2018-01-30 18:19:04 -08001328 # Generate payload.
1329 payload = Payload()
1330 payload.Generate(target_file, source_file)
Tao Baoc098e9e2016-01-07 13:03:56 -08001331
Tao Baoc7b403a2018-01-30 18:19:04 -08001332 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001333 payload_signer = PayloadSigner()
1334 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001335
Tao Baoc7b403a2018-01-30 18:19:04 -08001336 # Write the payload into output zip.
1337 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001338
Tao Baof7140c02018-01-30 17:09:24 -08001339 # Generate and include the secondary payload that installs secondary images
1340 # (e.g. system_other.img).
1341 if OPTIONS.include_secondary:
1342 # We always include a full payload for the secondary slot, even when
1343 # building an incremental OTA. See the comments for "--include_secondary".
1344 secondary_target_file = GetTargetFilesZipForSecondaryImages(target_file)
Tao Bao667ff572018-02-10 00:02:40 -08001345 secondary_payload = Payload(secondary=True)
Tao Baof7140c02018-01-30 17:09:24 -08001346 secondary_payload.Generate(secondary_target_file)
1347 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001348 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001349
Tianjie Xucfa86222016-03-07 16:31:19 -08001350 # If dm-verity is supported for the device, copy contents of care_map
1351 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001352 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001353 if (target_info.get("verity") == "true" or
1354 target_info.get("avb_enable") == "true"):
Tianjie Xucfa86222016-03-07 16:31:19 -08001355 care_map_path = "META/care_map.txt"
1356 namelist = target_zip.namelist()
1357 if care_map_path in namelist:
1358 care_map_data = target_zip.read(care_map_path)
Tao Baoc7b403a2018-01-30 18:19:04 -08001359 # In order to support streaming, care_map.txt needs to be packed as
1360 # ZIP_STORED.
Tao Baoc96316c2017-01-24 22:10:49 -08001361 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001362 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001363 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001364 print("Warning: cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001365
Tao Bao481bab82017-12-21 11:23:09 -08001366 # source_info must be None for full OTAs.
Tao Baobcd1d162017-08-26 13:10:26 -07001367 if source_file is None:
Tao Bao481bab82017-12-21 11:23:09 -08001368 assert source_info is None
Tao Bao21803d32017-04-19 10:16:09 -07001369
Tao Baobcd1d162017-08-26 13:10:26 -07001370 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001371 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001372
Tao Bao21803d32017-04-19 10:16:09 -07001373 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001374
Tao Bao2dd1c482017-02-03 16:49:39 -08001375 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001376 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001377 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001378 WriteMetadata(metadata, output_zip)
1379 common.ZipClose(output_zip)
1380
Tao Bao2dd1c482017-02-03 16:49:39 -08001381 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
Tao Bao89d7ab22017-12-14 17:05:33 -08001382 # ZIP entries, as well as padding the entry headers. We do a preliminary
Tao Bao2dd1c482017-02-03 16:49:39 -08001383 # signing (with an incomplete metadata entry) to allow that to happen. Then
Tao Bao89d7ab22017-12-14 17:05:33 -08001384 # compute the ZIP entry offsets, write back the final metadata and do the
Tao Bao2dd1c482017-02-03 16:49:39 -08001385 # final signing.
Tao Bao89d7ab22017-12-14 17:05:33 -08001386 prelim_signing = common.MakeTempFile(suffix='.zip')
1387 SignOutput(temp_zip_file.name, prelim_signing)
Tao Bao2dd1c482017-02-03 16:49:39 -08001388 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001389
Tao Bao2dd1c482017-02-03 16:49:39 -08001390 # Open the signed zip. Compute the final metadata that's needed for streaming.
Tao Bao89d7ab22017-12-14 17:05:33 -08001391 prelim_signing_zip = zipfile.ZipFile(prelim_signing, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001392 expected_length = len(metadata['ota-streaming-property-files'])
1393 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao89d7ab22017-12-14 17:05:33 -08001394 prelim_signing_zip, reserve_space=False, expected_length=expected_length)
1395 common.ZipClose(prelim_signing_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001396
Tao Bao89d7ab22017-12-14 17:05:33 -08001397 # Replace the METADATA entry.
1398 common.ZipDelete(prelim_signing, METADATA_NAME)
1399 output_zip = zipfile.ZipFile(prelim_signing, 'a',
Tao Bao2dd1c482017-02-03 16:49:39 -08001400 compression=zipfile.ZIP_DEFLATED)
Tao Bao2dd1c482017-02-03 16:49:39 -08001401 WriteMetadata(metadata, output_zip)
Tao Bao2dd1c482017-02-03 16:49:39 -08001402 common.ZipClose(output_zip)
1403
1404 # Re-sign the package after updating the metadata entry.
Tao Bao89d7ab22017-12-14 17:05:33 -08001405 SignOutput(prelim_signing, output_file)
Tao Bao2dd1c482017-02-03 16:49:39 -08001406
1407 # Reopen the final signed zip to double check the streaming metadata.
Tao Bao89d7ab22017-12-14 17:05:33 -08001408 output_zip = zipfile.ZipFile(output_file, 'r')
Tao Bao2dd1c482017-02-03 16:49:39 -08001409 actual = metadata['ota-streaming-property-files'].strip()
1410 expected = ComputeStreamingMetadata(output_zip)
1411 assert actual == expected, \
1412 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001413 common.ZipClose(output_zip)
1414
Tao Baoc098e9e2016-01-07 13:03:56 -08001415
Doug Zongkereef39442009-04-02 12:14:19 -07001416def main(argv):
1417
1418 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001419 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001420 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001421 elif o in ("-i", "--incremental_from"):
1422 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001423 elif o == "--full_radio":
1424 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001425 elif o == "--full_bootloader":
1426 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001427 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001428 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001429 elif o == "--downgrade":
1430 OPTIONS.downgrade = True
1431 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001432 elif o == "--override_timestamp":
1433 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001434 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001435 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001436 elif o == "--oem_no_mount":
1437 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001438 elif o in ("-e", "--extra_script"):
1439 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001440 elif o in ("-t", "--worker_threads"):
1441 if a.isdigit():
1442 OPTIONS.worker_threads = int(a)
1443 else:
1444 raise ValueError("Cannot parse value %r for option %r - only "
1445 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001446 elif o in ("-2", "--two_step"):
1447 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001448 elif o == "--include_secondary":
1449 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001450 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001451 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001452 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001453 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001454 elif o == "--block":
1455 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001456 elif o in ("-b", "--binary"):
1457 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001458 elif o == "--stash_threshold":
1459 try:
1460 OPTIONS.stash_threshold = float(a)
1461 except ValueError:
1462 raise ValueError("Cannot parse value %r for option %r - expecting "
1463 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001464 elif o == "--log_diff":
1465 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001466 elif o == "--payload_signer":
1467 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001468 elif o == "--payload_signer_args":
1469 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001470 elif o == "--extracted_input_target_files":
1471 OPTIONS.extracted_input = a
Doug Zongkereef39442009-04-02 12:14:19 -07001472 else:
1473 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001474 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001475
1476 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001477 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001478 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001479 "package_key=",
1480 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001481 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001482 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001483 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001484 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001485 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001486 "extra_script=",
1487 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001488 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001489 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001490 "no_signing",
1491 "block",
1492 "binary=",
1493 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001494 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001495 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001496 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001497 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001498 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001499 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001500 "extracted_input_target_files=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001501 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001502
1503 if len(args) != 2:
1504 common.Usage(__doc__)
1505 sys.exit(1)
1506
Tao Bao5d182562016-02-23 11:38:39 -08001507 if OPTIONS.downgrade:
1508 # Sanity check to enforce a data wipe.
1509 if not OPTIONS.wipe_user_data:
1510 raise ValueError("Cannot downgrade without a data wipe")
1511
1512 # We should only allow downgrading incrementals (as opposed to full).
1513 # Otherwise the device may go back from arbitrary build with this full
1514 # OTA package.
1515 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001516 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001517
Tao Bao3e6161a2017-02-28 11:48:48 -08001518 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1519 "Cannot have --downgrade AND --override_timestamp both"
1520
Tao Bao2db13852018-01-08 22:28:57 -08001521 # Load the build info dicts from the zip directly or the extracted input
1522 # directory. We don't need to unzip the entire target-files zips, because they
1523 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1524 # When loading the info dicts, we don't need to provide the second parameter
1525 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1526 # some properties with their actual paths, such as 'selinux_fc',
1527 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001528 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001529 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001530 else:
Tao Bao2db13852018-01-08 22:28:57 -08001531 with zipfile.ZipFile(args[0], 'r') as input_zip:
1532 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001533
Tao Bao2db13852018-01-08 22:28:57 -08001534 if OPTIONS.verbose:
1535 print("--- target info ---")
1536 common.DumpInfoDict(OPTIONS.info_dict)
1537
1538 # Load the source build dict if applicable.
1539 if OPTIONS.incremental_source is not None:
1540 OPTIONS.target_info_dict = OPTIONS.info_dict
1541 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
1542 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1543
1544 if OPTIONS.verbose:
1545 print("--- source info ---")
1546 common.DumpInfoDict(OPTIONS.source_info_dict)
1547
1548 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08001549 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1550
Tao Baoc098e9e2016-01-07 13:03:56 -08001551 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1552
Christian Oderf63e2cd2017-05-01 22:30:15 +02001553 # Use the default key to sign the package if not specified with package_key.
1554 # package_keys are needed on ab_updates, so always define them if an
1555 # ab_update is getting created.
1556 if not OPTIONS.no_signing or ab_update:
1557 if OPTIONS.package_key is None:
1558 OPTIONS.package_key = OPTIONS.info_dict.get(
1559 "default_system_dev_certificate",
1560 "build/target/product/security/testkey")
1561 # Get signing keys
1562 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1563
Tao Baoc098e9e2016-01-07 13:03:56 -08001564 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08001565 WriteABOTAPackageWithBrilloScript(
1566 target_file=args[0],
1567 output_file=args[1],
1568 source_file=OPTIONS.incremental_source)
1569
Tao Bao89fbb0f2017-01-10 10:47:58 -08001570 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08001571 return
1572
Tao Bao2db13852018-01-08 22:28:57 -08001573 # Sanity check the loaded info dicts first.
1574 if OPTIONS.info_dict.get("no_recovery") == "true":
1575 raise common.ExternalError(
1576 "--- target build has specified no recovery ---")
1577
1578 # Non-A/B OTAs rely on /cache partition to store temporary files.
1579 cache_size = OPTIONS.info_dict.get("cache_size")
1580 if cache_size is None:
1581 print("--- can't determine the cache partition size ---")
1582 OPTIONS.cache_size = cache_size
1583
Doug Zongker1c390a22009-05-14 19:06:36 -07001584 if OPTIONS.extra_script is not None:
1585 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1586
Dan Willemsencea5cd22017-03-21 14:44:27 -07001587 if OPTIONS.extracted_input is not None:
1588 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07001589 input_zip = zipfile.ZipFile(args[0], "r")
1590 else:
1591 print("unzipping target target-files...")
1592 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
1593 args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08001594 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001595
Tao Bao2db13852018-01-08 22:28:57 -08001596 # If the caller explicitly specified the device-specific extensions path via
1597 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
1598 # is present in the target target_files. Otherwise, take the path of the file
1599 # from 'tool_extensions' in the info dict and look for that in the local
1600 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07001601 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001602 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1603 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08001604 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001605 OPTIONS.device_specific = from_input
1606 else:
Tao Bao2db13852018-01-08 22:28:57 -08001607 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001608
Doug Zongker37974732010-09-16 17:44:38 -07001609 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001610 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001611
Tao Bao767e3ac2015-11-10 12:19:19 -08001612 # Set up the output zip. Create a temporary zip file if signing is needed.
1613 if OPTIONS.no_signing:
1614 if os.path.exists(args[1]):
1615 os.unlink(args[1])
1616 output_zip = zipfile.ZipFile(args[1], "w",
1617 compression=zipfile.ZIP_DEFLATED)
1618 else:
1619 temp_zip_file = tempfile.NamedTemporaryFile()
1620 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1621 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001622
Tao Bao767e3ac2015-11-10 12:19:19 -08001623 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08001624 if OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001625 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001626
Tao Bao32b80dc2018-01-08 22:50:47 -08001627 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08001628 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001629 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08001630 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08001631 OPTIONS.incremental_source,
Tao Bao457cbf62017-03-06 09:56:01 -08001632 UNZIP_PATTERN)
Tao Bao32b80dc2018-01-08 22:50:47 -08001633
1634 WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
1635
1636 if OPTIONS.log_diff:
1637 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08001638 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08001639 target_files_diff.recursiveDiff(
1640 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07001641
Tao Bao2db13852018-01-08 22:28:57 -08001642 common.ZipClose(input_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001643 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001644
Tao Bao767e3ac2015-11-10 12:19:19 -08001645 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001646 if not OPTIONS.no_signing:
1647 SignOutput(temp_zip_file.name, args[1])
1648 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001649
Tao Bao89fbb0f2017-01-10 10:47:58 -08001650 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07001651
1652
1653if __name__ == '__main__':
1654 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001655 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001656 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001657 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001658 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07001659 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001660 finally:
1661 common.Cleanup()