blob: 3fbcbcfe97f08a264b8460405f66e07f76df563b [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"""
Tao Bao30df8b42018-04-23 15:32:53 -070018Given a target-files zipfile, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
Doug Zongkereef39442009-04-02 12:14:19 -070020
Tao Bao30df8b42018-04-23 15:32:53 -070021Usage: ota_from_target_files [options] input_target_files output_ota_package
Doug Zongkereef39442009-04-02 12:14:19 -070022
Tao Bao30df8b42018-04-23 15:32:53 -070023Common options that apply to both of non-A/B and A/B OTAs
24
25 --downgrade
26 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070027 to an older one (e.g. downgrading from P preview back to O MR1).
28 "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29 will always be enforced when using this flag, so "ota-wipe=yes" will also
30 be included in the metadata file. The update-binary in the source build
31 will be used in the OTA package, unless --binary flag is specified. Please
32 also check the comment for --override_timestamp below.
Tao Bao30df8b42018-04-23 15:32:53 -070033
34 -i (--incremental_from) <file>
35 Generate an incremental OTA using the given target-files zip as the
36 starting build.
37
38 -k (--package_key) <key>
39 Key to use to sign the package (default is the value of
40 default_system_dev_certificate from the input target-files's
41 META/misc_info.txt, or "build/target/product/security/testkey" if that
42 value is not specified).
Doug Zongkerafb32ea2011-09-22 10:28:04 -070043
44 For incremental OTAs, the default value is based on the source
45 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070046
Tao Bao30df8b42018-04-23 15:32:53 -070047 --override_timestamp
48 Intentionally generate an incremental OTA that updates from a newer build
Tao Baofaa8e0b2018-04-12 14:31:43 -070049 to an older one (based on timestamp comparison), by setting the downgrade
50 flag in the package metadata. This differs from --downgrade flag, as we
51 don't enforce a data wipe with this flag. Because we know for sure this is
52 NOT an actual downgrade case, but two builds happen to be cut in a reverse
53 order (e.g. from two branches). A legit use case is that we cut a new
54 build C (after having A and B), but want to enfore an update path of A ->
55 C -> B. Specifying --downgrade may not help since that would enforce a
56 data wipe for C -> B update.
57
58 We used to set a fake timestamp in the package metadata for this flow. But
59 now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60 based on timestamp) with the same "ota-downgrade=yes" flag, with the
61 difference being whether "ota-wipe=yes" is set.
Doug Zongkereef39442009-04-02 12:14:19 -070062
Tao Bao30df8b42018-04-23 15:32:53 -070063 --wipe_user_data
64 Generate an OTA package that will wipe the user data partition when
65 installed.
66
Yifan Hong50e79542018-11-08 17:44:12 -080067 --retrofit_dynamic_partitions
68 Generates an OTA package that updates a device to support dynamic
69 partitions (default False). This flag is implied when generating
70 an incremental OTA where the base build does not support dynamic
71 partitions but the target build does. For A/B, when this flag is set,
72 --skip_postinstall is implied.
73
Tao Bao30df8b42018-04-23 15:32:53 -070074Non-A/B OTA specific options
75
76 -b (--binary) <file>
77 Use the given binary as the update-binary in the output package, instead
78 of the binary in the build's target_files. Use for development only.
79
80 --block
81 Generate a block-based OTA for non-A/B device. We have deprecated the
82 support for file-based OTA since O. Block-based OTA will be used by
83 default for all non-A/B devices. Keeping this flag here to not break
84 existing callers.
85
86 -e (--extra_script) <file>
87 Insert the contents of file at the end of the update script.
Tao Bao43078aa2015-04-21 14:32:35 -070088
leozwangaa6c1a12015-08-14 10:57:58 -070089 --full_bootloader
90 Similar to --full_radio. When generating an incremental OTA, always
91 include a full copy of bootloader image.
92
Tao Bao30df8b42018-04-23 15:32:53 -070093 --full_radio
94 When generating an incremental OTA, always include a full copy of radio
95 image. This option is only meaningful when -i is specified, because a full
96 radio is always included in a full OTA if applicable.
Michael Runge63f01de2014-10-28 19:24:19 -070097
Tao Bao30df8b42018-04-23 15:32:53 -070098 --log_diff <file>
99 Generate a log file that shows the differences in the source and target
100 builds for an incremental package. This option is only meaningful when -i
101 is specified.
102
103 -o (--oem_settings) <main_file[,additional_files...]>
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800104 Comma seperated list of files used to specify the expected OEM-specific
Tao Bao481bab82017-12-21 11:23:09 -0800105 properties on the OEM partition of the intended device. Multiple expected
106 values can be used by providing multiple files. Only the first dict will
107 be used to compute fingerprint, while the rest will be used to assert
108 OEM-specific properties.
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800109
Tao Bao8608cde2016-02-25 19:49:55 -0800110 --oem_no_mount
Tao Bao30df8b42018-04-23 15:32:53 -0700111 For devices with OEM-specific properties but without an OEM partition, do
112 not mount the OEM partition in the updater-script. This should be very
113 rarely used, since it's expected to have a dedicated OEM partition for
114 OEM-specific properties. Only meaningful when -o is specified.
Tao Bao8608cde2016-02-25 19:49:55 -0800115
Tao Bao30df8b42018-04-23 15:32:53 -0700116 --stash_threshold <float>
117 Specify the threshold that will be used to compute the maximum allowed
118 stash size (defaults to 0.8).
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700119
Tao Bao30df8b42018-04-23 15:32:53 -0700120 -t (--worker_threads) <int>
121 Specify the number of worker-threads that will be used when generating
122 patches for incremental updates (defaults to 3).
Tao Bao3e6161a2017-02-28 11:48:48 -0800123
Tao Bao30df8b42018-04-23 15:32:53 -0700124 --verify
125 Verify the checksums of the updated system and vendor (if any) partitions.
126 Non-A/B incremental OTAs only.
Doug Zongker1c390a22009-05-14 19:06:36 -0700127
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800128 -2 (--two_step)
Tao Bao30df8b42018-04-23 15:32:53 -0700129 Generate a 'two-step' OTA package, where recovery is updated first, so
130 that any changes made to the system partition are done using the new
131 recovery (new kernel, etc.).
132
133A/B OTA specific options
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800134
Tao Baof7140c02018-01-30 17:09:24 -0800135 --include_secondary
136 Additionally include the payload for secondary slot images (default:
137 False). Only meaningful when generating A/B OTAs.
138
139 By default, an A/B OTA package doesn't contain the images for the
140 secondary slot (e.g. system_other.img). Specifying this flag allows
141 generating a separate payload that will install secondary slot images.
142
143 Such a package needs to be applied in a two-stage manner, with a reboot
144 in-between. During the first stage, the updater applies the primary
145 payload only. Upon finishing, it reboots the device into the newly updated
146 slot. It then continues to install the secondary payload to the inactive
147 slot, but without switching the active slot at the end (needs the matching
148 support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
149
150 Due to the special install procedure, the secondary payload will be always
151 generated as a full payload.
152
Tao Baodea0f8b2016-06-20 17:55:06 -0700153 --payload_signer <signer>
154 Specify the signer when signing the payload and metadata for A/B OTAs.
155 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
156 with the package private key. If the private key cannot be accessed
157 directly, a payload signer that knows how to do that should be specified.
158 The signer will be supplied with "-inkey <path_to_key>",
159 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700160
161 --payload_signer_args <args>
162 Specify the arguments needed for payload signer.
Tao Bao15a146a2018-02-21 16:06:59 -0800163
164 --skip_postinstall
165 Skip the postinstall hooks when generating an A/B OTA package (default:
166 False). Note that this discards ALL the hooks, including non-optional
167 ones. Should only be used if caller knows it's safe to do so (e.g. all the
168 postinstall work is to dexopt apps and a data wipe will happen immediately
169 after). Only meaningful when generating A/B OTAs.
Doug Zongkereef39442009-04-02 12:14:19 -0700170"""
171
Tao Bao89fbb0f2017-01-10 10:47:58 -0800172from __future__ import print_function
173
Tao Bao32fcdab2018-10-12 10:30:39 -0700174import logging
Doug Zongkerfc44a512014-08-26 13:10:25 -0700175import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800176import os.path
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700177import shlex
Tao Bao15a146a2018-02-21 16:06:59 -0800178import shutil
Tao Bao85f16982018-03-08 16:28:33 -0800179import struct
Tao Bao481bab82017-12-21 11:23:09 -0800180import sys
Doug Zongkereef39442009-04-02 12:14:19 -0700181import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700182import zipfile
183
184import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700185import edify_generator
Tianjie Xu67c7cbb2018-08-30 00:32:07 -0700186import verity_utils
Doug Zongkereef39442009-04-02 12:14:19 -0700187
Tao Bao481bab82017-12-21 11:23:09 -0800188if sys.hexversion < 0x02070000:
189 print("Python 2.7 or newer is required.", file=sys.stderr)
190 sys.exit(1)
191
Tao Bao32fcdab2018-10-12 10:30:39 -0700192logger = logging.getLogger(__name__)
Tao Bao481bab82017-12-21 11:23:09 -0800193
Doug Zongkereef39442009-04-02 12:14:19 -0700194OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700195OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700196OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700197OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700198OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700199OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800200OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700201OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700202OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
203if OPTIONS.worker_threads == 0:
204 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800205OPTIONS.two_step = False
Tao Baof7140c02018-01-30 17:09:24 -0800206OPTIONS.include_secondary = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900207OPTIONS.no_signing = False
Tao Bao457cbf62017-03-06 09:56:01 -0800208OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -0800209OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700210OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800211OPTIONS.oem_no_mount = False
Tao Bao43078aa2015-04-21 14:32:35 -0700212OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700213OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700214# Stash size cannot exceed cache_size * threshold.
215OPTIONS.cache_size = None
216OPTIONS.stash_threshold = 0.8
Tao Baod62c6032015-11-30 09:40:20 -0800217OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700218OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700219OPTIONS.payload_signer_args = []
Tao Bao5f8ff932017-03-21 22:35:00 -0700220OPTIONS.extracted_input = None
Christian Oderf63e2cd2017-05-01 22:30:15 +0200221OPTIONS.key_passwords = []
Tao Bao15a146a2018-02-21 16:06:59 -0800222OPTIONS.skip_postinstall = False
Yifan Hong50e79542018-11-08 17:44:12 -0800223OPTIONS.retrofit_dynamic_partitions = False
Tao Bao15a146a2018-02-21 16:06:59 -0800224
Tao Bao8dcf7382015-05-21 14:09:49 -0700225
Tao Bao2dd1c482017-02-03 16:49:39 -0800226METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao15a146a2018-02-21 16:06:59 -0800227POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
Yifan Hong50e79542018-11-08 17:44:12 -0800228DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800229UNZIP_PATTERN = ['IMAGES/*', 'META/*']
Yifan Hong50e79542018-11-08 17:44:12 -0800230SUPER_SPLIT_PATTERN = ['OTA/super_*.img']
Tao Bao6b0b2f92017-03-05 11:38:11 -0800231
Tao Bao2dd1c482017-02-03 16:49:39 -0800232
Tao Bao481bab82017-12-21 11:23:09 -0800233class BuildInfo(object):
234 """A class that holds the information for a given build.
235
236 This class wraps up the property querying for a given source or target build.
237 It abstracts away the logic of handling OEM-specific properties, and caches
238 the commonly used properties such as fingerprint.
239
240 There are two types of info dicts: a) build-time info dict, which is generated
241 at build time (i.e. included in a target_files zip); b) OEM info dict that is
242 specified at package generation time (via command line argument
243 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
244 having "oem_fingerprint_properties" in build-time info dict), all the queries
245 would be answered based on build-time info dict only. Otherwise if using
246 OEM-specific properties, some of them will be calculated from two info dicts.
247
248 Users can query properties similarly as using a dict() (e.g. info['fstab']),
249 or to query build properties via GetBuildProp() or GetVendorBuildProp().
250
251 Attributes:
252 info_dict: The build-time info dict.
253 is_ab: Whether it's a build that uses A/B OTA.
254 oem_dicts: A list of OEM dicts.
255 oem_props: A list of OEM properties that should be read from OEM dicts; None
256 if the build doesn't use any OEM-specific property.
257 fingerprint: The fingerprint of the build, which would be calculated based
258 on OEM properties if applicable.
259 device: The device name, which could come from OEM dicts if applicable.
260 """
261
262 def __init__(self, info_dict, oem_dicts):
263 """Initializes a BuildInfo instance with the given dicts.
264
Tao Bao667c7532018-07-06 10:13:59 -0700265 Note that it only wraps up the given dicts, without making copies.
266
Tao Bao481bab82017-12-21 11:23:09 -0800267 Arguments:
268 info_dict: The build-time info dict.
269 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
270 that it always uses the first dict to calculate the fingerprint or the
271 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700272 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800273 """
274 self.info_dict = info_dict
275 self.oem_dicts = oem_dicts
276
277 self._is_ab = info_dict.get("ab_update") == "true"
278 self._oem_props = info_dict.get("oem_fingerprint_properties")
279
280 if self._oem_props:
281 assert oem_dicts, "OEM source required for this build"
282
283 # These two should be computed only after setting self._oem_props.
284 self._device = self.GetOemProperty("ro.product.device")
285 self._fingerprint = self.CalculateFingerprint()
286
287 @property
288 def is_ab(self):
289 return self._is_ab
290
291 @property
292 def device(self):
293 return self._device
294
295 @property
296 def fingerprint(self):
297 return self._fingerprint
298
299 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700300 def vendor_fingerprint(self):
301 if "vendor.build.prop" not in self.info_dict:
302 return None
303 vendor_build_prop = self.info_dict["vendor.build.prop"]
304 if "ro.vendor.build.fingerprint" in vendor_build_prop:
305 return vendor_build_prop["ro.vendor.build.fingerprint"]
306 if "ro.vendor.build.thumbprint" in vendor_build_prop:
307 return vendor_build_prop["ro.vendor.build.thumbprint"]
308 return None
309
310 @property
Tao Bao481bab82017-12-21 11:23:09 -0800311 def oem_props(self):
312 return self._oem_props
313
314 def __getitem__(self, key):
315 return self.info_dict[key]
316
Tao Bao667c7532018-07-06 10:13:59 -0700317 def __setitem__(self, key, value):
318 self.info_dict[key] = value
319
Tao Bao481bab82017-12-21 11:23:09 -0800320 def get(self, key, default=None):
321 return self.info_dict.get(key, default)
322
Tao Bao667c7532018-07-06 10:13:59 -0700323 def items(self):
324 return self.info_dict.items()
325
Tao Bao481bab82017-12-21 11:23:09 -0800326 def GetBuildProp(self, prop):
327 """Returns the inquired build property."""
328 try:
329 return self.info_dict.get("build.prop", {})[prop]
330 except KeyError:
331 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
332
333 def GetVendorBuildProp(self, prop):
334 """Returns the inquired vendor build property."""
335 try:
336 return self.info_dict.get("vendor.build.prop", {})[prop]
337 except KeyError:
338 raise common.ExternalError(
339 "couldn't find %s in vendor.build.prop" % (prop,))
340
341 def GetOemProperty(self, key):
342 if self.oem_props is not None and key in self.oem_props:
343 return self.oem_dicts[0][key]
344 return self.GetBuildProp(key)
345
346 def CalculateFingerprint(self):
347 if self.oem_props is None:
348 return self.GetBuildProp("ro.build.fingerprint")
349 return "%s/%s/%s:%s" % (
350 self.GetOemProperty("ro.product.brand"),
351 self.GetOemProperty("ro.product.name"),
352 self.GetOemProperty("ro.product.device"),
353 self.GetBuildProp("ro.build.thumbprint"))
354
355 def WriteMountOemScript(self, script):
356 assert self.oem_props is not None
357 recovery_mount_options = self.info_dict.get("recovery_mount_options")
358 script.Mount("/oem", recovery_mount_options)
359
360 def WriteDeviceAssertions(self, script, oem_no_mount):
361 # Read the property directly if not using OEM properties.
362 if not self.oem_props:
363 script.AssertDevice(self.device)
364 return
365
366 # Otherwise assert OEM properties.
367 if not self.oem_dicts:
368 raise common.ExternalError(
369 "No OEM file provided to answer expected assertions")
370
371 for prop in self.oem_props.split():
372 values = []
373 for oem_dict in self.oem_dicts:
374 if prop in oem_dict:
375 values.append(oem_dict[prop])
376 if not values:
377 raise common.ExternalError(
378 "The OEM file is missing the property %s" % (prop,))
379 script.AssertOemProperty(prop, values, oem_no_mount)
380
381
Tao Baofabe0832018-01-17 15:52:28 -0800382class PayloadSigner(object):
383 """A class that wraps the payload signing works.
384
385 When generating a Payload, hashes of the payload and metadata files will be
386 signed with the device key, either by calling an external payload signer or
387 by calling openssl with the package key. This class provides a unified
388 interface, so that callers can just call PayloadSigner.Sign().
389
390 If an external payload signer has been specified (OPTIONS.payload_signer), it
391 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
392 that the signing key should be provided as part of the payload_signer_args.
393 Otherwise without an external signer, it uses the package key
394 (OPTIONS.package_key) and calls openssl for the signing works.
395 """
396
397 def __init__(self):
398 if OPTIONS.payload_signer is None:
399 # Prepare the payload signing key.
400 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
401 pw = OPTIONS.key_passwords[OPTIONS.package_key]
402
403 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
404 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
405 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
406 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700407 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800408
409 self.signer = "openssl"
410 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
411 "-pkeyopt", "digest:sha256"]
412 else:
413 self.signer = OPTIONS.payload_signer
414 self.signer_args = OPTIONS.payload_signer_args
415
416 def Sign(self, in_file):
417 """Signs the given input file. Returns the output filename."""
418 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
419 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700420 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800421 return out_file
422
423
Tao Bao40b18822018-01-30 18:19:04 -0800424class Payload(object):
425 """Manages the creation and the signing of an A/B OTA Payload."""
426
427 PAYLOAD_BIN = 'payload.bin'
428 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800429 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
430 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800431
Tao Bao667ff572018-02-10 00:02:40 -0800432 def __init__(self, secondary=False):
433 """Initializes a Payload instance.
434
435 Args:
436 secondary: Whether it's generating a secondary payload (default: False).
437 """
Tao Bao40b18822018-01-30 18:19:04 -0800438 self.payload_file = None
439 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800440 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800441
442 def Generate(self, target_file, source_file=None, additional_args=None):
443 """Generates a payload from the given target-files zip(s).
444
445 Args:
446 target_file: The filename of the target build target-files zip.
447 source_file: The filename of the source build target-files zip; or None if
448 generating a full OTA.
449 additional_args: A list of additional args that should be passed to
450 brillo_update_payload script; or None.
451 """
452 if additional_args is None:
453 additional_args = []
454
455 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
456 cmd = ["brillo_update_payload", "generate",
457 "--payload", payload_file,
458 "--target_image", target_file]
459 if source_file is not None:
460 cmd.extend(["--source_image", source_file])
461 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700462 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800463
464 self.payload_file = payload_file
465 self.payload_properties = None
466
467 def Sign(self, payload_signer):
468 """Generates and signs the hashes of the payload and metadata.
469
470 Args:
471 payload_signer: A PayloadSigner() instance that serves the signing work.
472
473 Raises:
474 AssertionError: On any failure when calling brillo_update_payload script.
475 """
476 assert isinstance(payload_signer, PayloadSigner)
477
478 # 1. Generate hashes of the payload and metadata files.
479 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
480 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
481 cmd = ["brillo_update_payload", "hash",
482 "--unsigned_payload", self.payload_file,
483 "--signature_size", "256",
484 "--metadata_hash_file", metadata_sig_file,
485 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700486 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800487
488 # 2. Sign the hashes.
489 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
490 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
491
492 # 3. Insert the signatures back into the payload file.
493 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
494 suffix=".bin")
495 cmd = ["brillo_update_payload", "sign",
496 "--unsigned_payload", self.payload_file,
497 "--payload", signed_payload_file,
498 "--signature_size", "256",
499 "--metadata_signature_file", signed_metadata_sig_file,
500 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700501 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800502
503 # 4. Dump the signed payload properties.
504 properties_file = common.MakeTempFile(prefix="payload-properties-",
505 suffix=".txt")
506 cmd = ["brillo_update_payload", "properties",
507 "--payload", signed_payload_file,
508 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700509 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800510
Tao Bao667ff572018-02-10 00:02:40 -0800511 if self.secondary:
512 with open(properties_file, "a") as f:
513 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
514
Tao Bao40b18822018-01-30 18:19:04 -0800515 if OPTIONS.wipe_user_data:
516 with open(properties_file, "a") as f:
517 f.write("POWERWASH=1\n")
518
519 self.payload_file = signed_payload_file
520 self.payload_properties = properties_file
521
Tao Bao667ff572018-02-10 00:02:40 -0800522 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800523 """Writes the payload to the given zip.
524
525 Args:
526 output_zip: The output ZipFile instance.
527 """
528 assert self.payload_file is not None
529 assert self.payload_properties is not None
530
Tao Bao667ff572018-02-10 00:02:40 -0800531 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800532 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
533 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
534 else:
535 payload_arcname = Payload.PAYLOAD_BIN
536 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
537
Tao Bao40b18822018-01-30 18:19:04 -0800538 # Add the signed payload file and properties into the zip. In order to
539 # support streaming, we pack them as ZIP_STORED. So these entries can be
540 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800541 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800542 compress_type=zipfile.ZIP_STORED)
543 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800544 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800545 compress_type=zipfile.ZIP_STORED)
546
547
Doug Zongkereef39442009-04-02 12:14:19 -0700548def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200549 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700550
Doug Zongker951495f2009-08-14 12:44:19 -0700551 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
552 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700553
554
Tao Bao481bab82017-12-21 11:23:09 -0800555def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800556 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800557 if not oem_source:
558 return None
559
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800560 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800561 for oem_file in oem_source:
562 with open(oem_file) as fp:
563 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800564 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700565
Doug Zongkereef39442009-04-02 12:14:19 -0700566
Tao Baod42e97e2016-11-30 12:11:57 -0800567def _WriteRecoveryImageToBoot(script, output_zip):
568 """Find and write recovery image to /boot in two-step OTA.
569
570 In two-step OTAs, we write recovery image to /boot as the first step so that
571 we can reboot to there and install a new recovery image to /recovery.
572 A special "recovery-two-step.img" will be preferred, which encodes the correct
573 path of "/boot". Otherwise the device may show "device is corrupt" message
574 when booting into /boot.
575
576 Fall back to using the regular recovery.img if the two-step recovery image
577 doesn't exist. Note that rebuilding the special image at this point may be
578 infeasible, because we don't have the desired boot signer and keys when
579 calling ota_from_target_files.py.
580 """
581
582 recovery_two_step_img_name = "recovery-two-step.img"
583 recovery_two_step_img_path = os.path.join(
584 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
585 if os.path.exists(recovery_two_step_img_path):
586 recovery_two_step_img = common.GetBootableImage(
587 recovery_two_step_img_name, recovery_two_step_img_name,
588 OPTIONS.input_tmp, "RECOVERY")
589 common.ZipWriteStr(
590 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700591 logger.info(
592 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800593 script.WriteRawImage("/boot", recovery_two_step_img_name)
594 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700595 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800596 # The "recovery.img" entry has been written into package earlier.
597 script.WriteRawImage("/boot", "recovery.img")
598
599
Doug Zongkerc9253822014-02-04 12:17:58 -0800600def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700601 namelist = [name for name in target_files_zip.namelist()]
602 return ("SYSTEM/recovery-from-boot.p" in namelist or
603 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700604
Tao Bao457cbf62017-03-06 09:56:01 -0800605
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700606def HasVendorPartition(target_files_zip):
607 try:
608 target_files_zip.getinfo("VENDOR/")
609 return True
610 except KeyError:
611 return False
612
Tao Bao457cbf62017-03-06 09:56:01 -0800613
Tao Bao481bab82017-12-21 11:23:09 -0800614def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700615 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800616 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700617
618
Tao Bao481bab82017-12-21 11:23:09 -0800619def WriteFingerprintAssertion(script, target_info, source_info):
620 source_oem_props = source_info.oem_props
621 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700622
Tao Bao481bab82017-12-21 11:23:09 -0800623 if source_oem_props is None and target_oem_props is None:
624 script.AssertSomeFingerprint(
625 source_info.fingerprint, target_info.fingerprint)
626 elif source_oem_props is not None and target_oem_props is not None:
627 script.AssertSomeThumbprint(
628 target_info.GetBuildProp("ro.build.thumbprint"),
629 source_info.GetBuildProp("ro.build.thumbprint"))
630 elif source_oem_props is None and target_oem_props is not None:
631 script.AssertFingerprintOrThumbprint(
632 source_info.fingerprint,
633 target_info.GetBuildProp("ro.build.thumbprint"))
634 else:
635 script.AssertFingerprintOrThumbprint(
636 target_info.fingerprint,
637 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700638
Doug Zongkerfc44a512014-08-26 13:10:25 -0700639
Tao Bao481bab82017-12-21 11:23:09 -0800640def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
641 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700642 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700643
644 Metadata used for on-device compatibility verification is retrieved from
645 target_zip then added to compatibility.zip which is added to the output_zip
646 archive.
647
Tao Baobcd1d162017-08-26 13:10:26 -0700648 Compatibility archive should only be included for devices that have enabled
649 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700650
651 Args:
652 target_zip: Zip file containing the source files to be included for OTA.
653 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800654 target_info: The BuildInfo instance that holds the target build info.
655 source_info: The BuildInfo instance that holds the source build info, if
656 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700657 """
658
Tao Baobcd1d162017-08-26 13:10:26 -0700659 def AddCompatibilityArchive(system_updated, vendor_updated):
660 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700661
Tao Baobcd1d162017-08-26 13:10:26 -0700662 Args:
663 system_updated: If True, the system image will be updated and therefore
664 its metadata should be included.
665 vendor_updated: If True, the vendor image will be updated and therefore
666 its metadata should be included.
667 """
668 # Determine what metadata we need. Files are names relative to META/.
669 compatibility_files = []
670 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
671 system_metadata = ("system_manifest.xml", "system_matrix.xml")
672 if vendor_updated:
673 compatibility_files += vendor_metadata
674 if system_updated:
675 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700676
Tao Baobcd1d162017-08-26 13:10:26 -0700677 # Create new archive.
678 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800679 compatibility_archive_zip = zipfile.ZipFile(
680 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700681
Tao Baobcd1d162017-08-26 13:10:26 -0700682 # Add metadata.
683 for file_name in compatibility_files:
684 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700685
Tao Baobcd1d162017-08-26 13:10:26 -0700686 if target_file_name in target_zip.namelist():
687 data = target_zip.read(target_file_name)
688 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700689
Tao Baobcd1d162017-08-26 13:10:26 -0700690 # Ensure files are written before we copy into output_zip.
691 compatibility_archive_zip.close()
692
693 # Only add the archive if we have any compatibility info.
694 if compatibility_archive_zip.namelist():
695 common.ZipWrite(output_zip, compatibility_archive.name,
696 arcname="compatibility.zip",
697 compress_type=zipfile.ZIP_STORED)
698
699 # Will only proceed if the target has enabled the Treble support (as well as
700 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800701 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700702 return
703
Tao Baobcd1d162017-08-26 13:10:26 -0700704 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800705 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700706 AddCompatibilityArchive(True, True)
707 return
708
Tao Bao481bab82017-12-21 11:23:09 -0800709 source_fp = source_info.fingerprint
710 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700711 system_updated = source_fp != target_fp
712
Tao Baoea6cbd02018-09-05 13:06:37 -0700713 source_fp_vendor = source_info.vendor_fingerprint
714 target_fp_vendor = target_info.vendor_fingerprint
715 # vendor build fingerprints could be possibly blacklisted at build time. For
716 # such a case, we consider the vendor images being changed.
717 if source_fp_vendor is None or target_fp_vendor is None:
718 vendor_updated = True
719 else:
720 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700721
722 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700723
724
Tao Bao491d7e22018-02-21 13:17:22 -0800725def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800726 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700727
Tao Bao481bab82017-12-21 11:23:09 -0800728 # We don't know what version it will be installed on top of. We expect the API
729 # just won't change very often. Similarly for fstab, it might have changed in
730 # the target build.
731 target_api_version = target_info["recovery_api_version"]
732 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700733
Tao Bao481bab82017-12-21 11:23:09 -0800734 if target_info.oem_props and not OPTIONS.oem_no_mount:
735 target_info.WriteMountOemScript(script)
736
Tao Baodf3a48b2018-01-10 16:30:43 -0800737 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700738
Tao Bao491d7e22018-02-21 13:17:22 -0800739 if not OPTIONS.no_signing:
740 staging_file = common.MakeTempFile(suffix='.zip')
741 else:
742 staging_file = output_file
743
744 output_zip = zipfile.ZipFile(
745 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
746
Doug Zongker05d3dea2009-06-22 11:32:31 -0700747 device_specific = common.DeviceSpecificParams(
748 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800749 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700750 output_zip=output_zip,
751 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700752 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700753 metadata=metadata,
754 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700755
Tao Bao457cbf62017-03-06 09:56:01 -0800756 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800757
Tao Bao481bab82017-12-21 11:23:09 -0800758 # Assertions (e.g. downgrade check, device properties check).
759 ts = target_info.GetBuildProp("ro.build.date.utc")
760 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700761 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700762
Tao Bao481bab82017-12-21 11:23:09 -0800763 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700764 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800765
766 # Two-step package strategy (in chronological order, which is *not*
767 # the order in which the generated script has things):
768 #
769 # if stage is not "2/3" or "3/3":
770 # write recovery image to boot partition
771 # set stage to "2/3"
772 # reboot to boot partition and restart recovery
773 # else if stage is "2/3":
774 # write recovery image to recovery partition
775 # set stage to "3/3"
776 # reboot to recovery partition and restart recovery
777 # else:
778 # (stage must be "3/3")
779 # set stage to ""
780 # do normal full package installation:
781 # wipe and install system, boot image, etc.
782 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700783 # complete script normally
784 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800785
786 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
787 OPTIONS.input_tmp, "RECOVERY")
788 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800789 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800790 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800791 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800792 assert fs.fs_type.upper() == "EMMC", \
793 "two-step packages only supported on devices with EMMC /misc partitions"
794 bcb_dev = {"bcb_dev": fs.device}
795 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
796 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700797if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800798""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800799
800 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
801 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800802 script.WriteRawImage("/recovery", "recovery.img")
803 script.AppendExtra("""
804set_stage("%(bcb_dev)s", "3/3");
805reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700806else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800807""" % bcb_dev)
808
Tao Baod42e97e2016-11-30 12:11:57 -0800809 # Stage 3/3: Make changes.
810 script.Comment("Stage 3/3")
811
Tao Bao6c55a8a2015-04-08 15:30:27 -0700812 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800813 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700814
Doug Zongkere5ff5902012-01-17 10:55:37 -0800815 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700816
Doug Zongker01ce19c2014-02-04 13:48:15 -0800817 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700818
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700819 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800820 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700821 if HasVendorPartition(input_zip):
822 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700823
Doug Zongker4b9596f2014-06-09 14:15:45 -0700824 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800825
Tao Baoe709b092018-02-07 12:40:00 -0800826 # See the notes in WriteBlockIncrementalOTAPackage().
827 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
828
Yifan Hong10c530d2018-12-27 17:34:18 -0800829 def GetBlockDifference(partition):
830 # Full OTA is done as an "incremental" against an empty source image. This
831 # has the effect of writing new data from the package to the entire
832 # partition, but lets us reuse the updater code that writes incrementals to
833 # do it.
834 tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
835 allow_shared_blocks)
836 tgt.ResetFileMap()
837 diff = common.BlockDifference(partition, tgt, src=None)
838 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700839
Yifan Hong10c530d2018-12-27 17:34:18 -0800840 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
841 if device_specific_diffs:
842 assert all(isinstance(diff, common.BlockDifference)
843 for diff in device_specific_diffs), \
844 "FullOTA_GetBlockDifferences is not returning a list of " \
845 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800846
Yifan Hong10c530d2018-12-27 17:34:18 -0800847 progress_dict = dict()
848 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700849 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800850 block_diffs.append(GetBlockDifference("vendor"))
851 progress_dict["vendor"] = 0.1
852 if device_specific_diffs:
853 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700854
Yifan Hong10c530d2018-12-27 17:34:18 -0800855 if target_info.get('use_dynamic_partitions') == "true":
856 # Use empty source_info_dict to indicate that all partitions / groups must
857 # be re-added.
858 dynamic_partitions_diff = common.DynamicPartitionsDifference(
859 info_dict=OPTIONS.info_dict,
860 block_diffs=block_diffs,
861 progress_dict=progress_dict)
862 dynamic_partitions_diff.WriteScript(script, output_zip,
863 write_verify_script=OPTIONS.verify)
864 else:
865 for block_diff in block_diffs:
866 block_diff.WriteScript(script, output_zip,
867 progress=progress_dict.get(block_diff.partition),
868 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700869
Tao Bao481bab82017-12-21 11:23:09 -0800870 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700871
Yifan Hong10c530d2018-12-27 17:34:18 -0800872 boot_img = common.GetBootableImage(
873 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800874 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700875 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700876
Doug Zongker01ce19c2014-02-04 13:48:15 -0800877 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700878 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700879
Doug Zongker01ce19c2014-02-04 13:48:15 -0800880 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700881 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700882
Doug Zongker1c390a22009-05-14 19:06:36 -0700883 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700884 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700885
Doug Zongker14833602010-02-02 13:12:04 -0800886 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800887
Doug Zongker922206e2014-03-04 13:16:24 -0800888 if OPTIONS.wipe_user_data:
889 script.ShowProgress(0.1, 10)
890 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700891
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800892 if OPTIONS.two_step:
893 script.AppendExtra("""
894set_stage("%(bcb_dev)s", "");
895""" % bcb_dev)
896 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800897
898 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
899 script.Comment("Stage 1/3")
900 _WriteRecoveryImageToBoot(script, output_zip)
901
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800902 script.AppendExtra("""
903set_stage("%(bcb_dev)s", "2/3");
904reboot_now("%(bcb_dev)s", "");
905endif;
906endif;
907""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800908
Tao Bao5d182562016-02-23 11:38:39 -0800909 script.SetProgress(1)
910 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800911 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800912
913 # We haven't written the metadata entry, which will be done in
914 # FinalizeMetadata.
915 common.ZipClose(output_zip)
916
917 needed_property_files = (
918 NonAbOtaPropertyFiles(),
919 )
920 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700921
Doug Zongkerfc44a512014-08-26 13:10:25 -0700922
Doug Zongker2ea21062010-04-28 16:05:21 -0700923def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800924 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
925 common.ZipWriteStr(output_zip, METADATA_NAME, value,
926 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700927
Doug Zongkerfc44a512014-08-26 13:10:25 -0700928
Tao Bao481bab82017-12-21 11:23:09 -0800929def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800930 # Only incremental OTAs are allowed to reach here.
931 assert OPTIONS.incremental_source is not None
932
Tao Bao481bab82017-12-21 11:23:09 -0800933 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
934 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800935 is_downgrade = long(post_timestamp) < long(pre_timestamp)
936
937 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800938 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700939 raise RuntimeError(
940 "--downgrade or --override_timestamp specified but no downgrade "
941 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800942 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800943 else:
944 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700945 raise RuntimeError(
946 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
947 "Need to specify --override_timestamp OR --downgrade to allow "
948 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800949
950
Tao Baodf3a48b2018-01-10 16:30:43 -0800951def GetPackageMetadata(target_info, source_info=None):
952 """Generates and returns the metadata dict.
953
954 It generates a dict() that contains the info to be written into an OTA
955 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700956 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800957
958 Args:
959 target_info: The BuildInfo instance that holds the target build info.
960 source_info: The BuildInfo instance that holds the source build info, or
961 None if generating full OTA.
962
963 Returns:
964 A dict to be written into package metadata entry.
965 """
966 assert isinstance(target_info, BuildInfo)
967 assert source_info is None or isinstance(source_info, BuildInfo)
968
969 metadata = {
970 'post-build' : target_info.fingerprint,
971 'post-build-incremental' : target_info.GetBuildProp(
972 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800973 'post-sdk-level' : target_info.GetBuildProp(
974 'ro.build.version.sdk'),
975 'post-security-patch-level' : target_info.GetBuildProp(
976 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800977 }
978
979 if target_info.is_ab:
980 metadata['ota-type'] = 'AB'
981 metadata['ota-required-cache'] = '0'
982 else:
983 metadata['ota-type'] = 'BLOCK'
984
985 if OPTIONS.wipe_user_data:
986 metadata['ota-wipe'] = 'yes'
987
988 is_incremental = source_info is not None
989 if is_incremental:
990 metadata['pre-build'] = source_info.fingerprint
991 metadata['pre-build-incremental'] = source_info.GetBuildProp(
992 'ro.build.version.incremental')
993 metadata['pre-device'] = source_info.device
994 else:
995 metadata['pre-device'] = target_info.device
996
Tao Baofaa8e0b2018-04-12 14:31:43 -0700997 # Use the actual post-timestamp, even for a downgrade case.
998 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
999
1000 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001001 if is_incremental:
1002 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001003
1004 return metadata
1005
1006
Tao Baod3fc38a2018-03-08 16:09:01 -08001007class PropertyFiles(object):
1008 """A class that computes the property-files string for an OTA package.
1009
1010 A property-files string is a comma-separated string that contains the
1011 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1012 can be fetched directly with the package URL along with the offset/size info.
1013 These strings can be used for streaming A/B OTAs, or allowing an updater to
1014 download package metadata entry directly, without paying the cost of
1015 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001016
Tao Baocc8e2662018-03-01 19:30:00 -08001017 Computing the final property-files string requires two passes. Because doing
1018 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1019 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1020 values.
1021
1022 This class provides functions to be called for each pass. The general flow is
1023 as follows.
1024
Tao Baod3fc38a2018-03-08 16:09:01 -08001025 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001026 # The first pass, which writes placeholders before doing initial signing.
1027 property_files.Compute()
1028 SignOutput()
1029
1030 # The second pass, by replacing the placeholders with actual data.
1031 property_files.Finalize()
1032 SignOutput()
1033
1034 And the caller can additionally verify the final result.
1035
1036 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001037 """
1038
Tao Baocc8e2662018-03-01 19:30:00 -08001039 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001040 self.name = None
1041 self.required = ()
1042 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001043
Tao Baocc8e2662018-03-01 19:30:00 -08001044 def Compute(self, input_zip):
1045 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001046
Tao Baocc8e2662018-03-01 19:30:00 -08001047 We reserve extra space for the offset and size of the metadata entry itself,
1048 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001049
Tao Baocc8e2662018-03-01 19:30:00 -08001050 Args:
1051 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001052
Tao Baocc8e2662018-03-01 19:30:00 -08001053 Returns:
1054 A string with placeholders for the metadata offset/size info, e.g.
1055 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1056 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001057 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001058
Tao Baod2ce2ed2018-03-16 12:59:42 -07001059 class InsufficientSpaceException(Exception):
1060 pass
1061
Tao Baocc8e2662018-03-01 19:30:00 -08001062 def Finalize(self, input_zip, reserved_length):
1063 """Finalizes a property-files string with actual METADATA offset/size info.
1064
1065 The input ZIP file has been signed, with the ZIP entries in the desired
1066 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1067 the ZIP entry offsets and construct the property-files string with actual
1068 data. Note that during this process, we must pad the property-files string
1069 to the reserved length, so that the METADATA entry size remains the same.
1070 Otherwise the entries' offsets and sizes may change again.
1071
1072 Args:
1073 input_zip: The input ZIP file.
1074 reserved_length: The reserved length of the property-files string during
1075 the call to Compute(). The final string must be no more than this
1076 size.
1077
1078 Returns:
1079 A property-files string including the metadata offset/size info, e.g.
1080 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1081
1082 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001083 InsufficientSpaceException: If the reserved length is insufficient to hold
1084 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001085 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001086 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001087 if len(result) > reserved_length:
1088 raise self.InsufficientSpaceException(
1089 'Insufficient reserved space: reserved={}, actual={}'.format(
1090 reserved_length, len(result)))
1091
Tao Baocc8e2662018-03-01 19:30:00 -08001092 result += ' ' * (reserved_length - len(result))
1093 return result
1094
1095 def Verify(self, input_zip, expected):
1096 """Verifies the input ZIP file contains the expected property-files string.
1097
1098 Args:
1099 input_zip: The input ZIP file.
1100 expected: The property-files string that's computed from Finalize().
1101
1102 Raises:
1103 AssertionError: On finding a mismatch.
1104 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001105 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001106 assert actual == expected, \
1107 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1108
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001109 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1110 """
1111 Constructs the property-files string per request.
1112
1113 Args:
1114 zip_file: The input ZIP file.
1115 reserved_length: The reserved length of the property-files string.
1116
1117 Returns:
1118 A property-files string including the metadata offset/size info, e.g.
1119 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1120 """
Tao Baocc8e2662018-03-01 19:30:00 -08001121
1122 def ComputeEntryOffsetSize(name):
1123 """Computes the zip entry offset and size."""
1124 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001125 offset = info.header_offset
1126 offset += zipfile.sizeFileHeader
1127 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001128 size = info.file_size
1129 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1130
1131 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001132 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001133 for entry in self.required:
1134 tokens.append(ComputeEntryOffsetSize(entry))
1135 for entry in self.optional:
1136 if entry in zip_file.namelist():
1137 tokens.append(ComputeEntryOffsetSize(entry))
1138
1139 # 'META-INF/com/android/metadata' is required. We don't know its actual
1140 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001141 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1142 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1143 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1144 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001145 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001146 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001147 else:
1148 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1149
1150 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001151
Tao Bao85f16982018-03-08 16:28:33 -08001152 def _GetPrecomputed(self, input_zip):
1153 """Computes the additional tokens to be included into the property-files.
1154
1155 This applies to tokens without actual ZIP entries, such as
1156 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1157 that they can download the payload metadata directly with the info.
1158
1159 Args:
1160 input_zip: The input zip file.
1161
1162 Returns:
1163 A list of strings (tokens) to be added to the property-files string.
1164 """
1165 # pylint: disable=no-self-use
1166 # pylint: disable=unused-argument
1167 return []
1168
Tao Baofe5b69a2018-03-02 09:47:43 -08001169
Tao Baod3fc38a2018-03-08 16:09:01 -08001170class StreamingPropertyFiles(PropertyFiles):
1171 """A subclass for computing the property-files for streaming A/B OTAs."""
1172
1173 def __init__(self):
1174 super(StreamingPropertyFiles, self).__init__()
1175 self.name = 'ota-streaming-property-files'
1176 self.required = (
1177 # payload.bin and payload_properties.txt must exist.
1178 'payload.bin',
1179 'payload_properties.txt',
1180 )
1181 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001182 # care_map is available only if dm-verity is enabled.
1183 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001184 'care_map.txt',
1185 # compatibility.zip is available only if target supports Treble.
1186 'compatibility.zip',
1187 )
1188
1189
Tao Bao85f16982018-03-08 16:28:33 -08001190class AbOtaPropertyFiles(StreamingPropertyFiles):
1191 """The property-files for A/B OTA that includes payload_metadata.bin info.
1192
1193 Since P, we expose one more token (aka property-file), in addition to the ones
1194 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1195 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1196 doesn't exist as a separate ZIP entry, but can be used to verify if the
1197 payload can be applied on the given device.
1198
1199 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1200 and the newly added 'ota-property-files' in P. The new token will only be
1201 available in 'ota-property-files'.
1202 """
1203
1204 def __init__(self):
1205 super(AbOtaPropertyFiles, self).__init__()
1206 self.name = 'ota-property-files'
1207
1208 def _GetPrecomputed(self, input_zip):
1209 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1210 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1211
1212 @staticmethod
1213 def _GetPayloadMetadataOffsetAndSize(input_zip):
1214 """Computes the offset and size of the payload metadata for a given package.
1215
1216 (From system/update_engine/update_metadata.proto)
1217 A delta update file contains all the deltas needed to update a system from
1218 one specific version to another specific version. The update format is
1219 represented by this struct pseudocode:
1220
1221 struct delta_update_file {
1222 char magic[4] = "CrAU";
1223 uint64 file_format_version;
1224 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1225
1226 // Only present if format_version > 1:
1227 uint32 metadata_signature_size;
1228
1229 // The Bzip2 compressed DeltaArchiveManifest
1230 char manifest[metadata_signature_size];
1231
1232 // The signature of the metadata (from the beginning of the payload up to
1233 // this location, not including the signature itself). This is a
1234 // serialized Signatures message.
1235 char medatada_signature_message[metadata_signature_size];
1236
1237 // Data blobs for files, no specific format. The specific offset
1238 // and length of each data blob is recorded in the DeltaArchiveManifest.
1239 struct {
1240 char data[];
1241 } blobs[];
1242
1243 // These two are not signed:
1244 uint64 payload_signatures_message_size;
1245 char payload_signatures_message[];
1246 };
1247
1248 'payload-metadata.bin' contains all the bytes from the beginning of the
1249 payload, till the end of 'medatada_signature_message'.
1250 """
1251 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001252 payload_offset = payload_info.header_offset
1253 payload_offset += zipfile.sizeFileHeader
1254 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001255 payload_size = payload_info.file_size
1256
1257 with input_zip.open('payload.bin', 'r') as payload_fp:
1258 header_bin = payload_fp.read(24)
1259
1260 # network byte order (big-endian)
1261 header = struct.unpack("!IQQL", header_bin)
1262
1263 # 'CrAU'
1264 magic = header[0]
1265 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1266
1267 manifest_size = header[2]
1268 metadata_signature_size = header[3]
1269 metadata_total = 24 + manifest_size + metadata_signature_size
1270 assert metadata_total < payload_size
1271
1272 return (payload_offset, metadata_total)
1273
1274
Tao Bao491d7e22018-02-21 13:17:22 -08001275class NonAbOtaPropertyFiles(PropertyFiles):
1276 """The property-files for non-A/B OTA.
1277
1278 For non-A/B OTA, the property-files string contains the info for METADATA
1279 entry, with which a system updater can be fetched the package metadata prior
1280 to downloading the entire package.
1281 """
1282
1283 def __init__(self):
1284 super(NonAbOtaPropertyFiles, self).__init__()
1285 self.name = 'ota-property-files'
1286
1287
Tao Baod3fc38a2018-03-08 16:09:01 -08001288def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001289 """Finalizes the metadata and signs an A/B OTA package.
1290
1291 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1292 that contains the offsets and sizes for the ZIP entries. An example
1293 property-files string is as follows.
1294
1295 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1296
1297 OTA server can pass down this string, in addition to the package URL, to the
1298 system update client. System update client can then fetch individual ZIP
1299 entries (ZIP_STORED) directly at the given offset of the URL.
1300
1301 Args:
1302 metadata: The metadata dict for the package.
1303 input_file: The input ZIP filename that doesn't contain the package METADATA
1304 entry yet.
1305 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001306 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001307 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001308
Tao Baod2ce2ed2018-03-16 12:59:42 -07001309 def ComputeAllPropertyFiles(input_file, needed_property_files):
1310 # Write the current metadata entry with placeholders.
1311 with zipfile.ZipFile(input_file) as input_zip:
1312 for property_files in needed_property_files:
1313 metadata[property_files.name] = property_files.Compute(input_zip)
1314 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001315
Tao Baod2ce2ed2018-03-16 12:59:42 -07001316 if METADATA_NAME in namelist:
1317 common.ZipDelete(input_file, METADATA_NAME)
1318 output_zip = zipfile.ZipFile(input_file, 'a')
1319 WriteMetadata(metadata, output_zip)
1320 common.ZipClose(output_zip)
1321
1322 if OPTIONS.no_signing:
1323 return input_file
1324
Tao Bao491d7e22018-02-21 13:17:22 -08001325 prelim_signing = common.MakeTempFile(suffix='.zip')
1326 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001327 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001328
Tao Baod2ce2ed2018-03-16 12:59:42 -07001329 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1330 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1331 for property_files in needed_property_files:
1332 metadata[property_files.name] = property_files.Finalize(
1333 prelim_signing_zip, len(metadata[property_files.name]))
1334
1335 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1336 # entries, as well as padding the entry headers. We do a preliminary signing
1337 # (with an incomplete metadata entry) to allow that to happen. Then compute
1338 # the ZIP entry offsets, write back the final metadata and do the final
1339 # signing.
1340 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1341 try:
1342 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1343 except PropertyFiles.InsufficientSpaceException:
1344 # Even with the preliminary signing, the entry orders may change
1345 # dramatically, which leads to insufficiently reserved space during the
1346 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1347 # preliminary signing works, based on the already ordered ZIP entries, to
1348 # address the issue.
1349 prelim_signing = ComputeAllPropertyFiles(
1350 prelim_signing, needed_property_files)
1351 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001352
1353 # Replace the METADATA entry.
1354 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001355 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001356 WriteMetadata(metadata, output_zip)
1357 common.ZipClose(output_zip)
1358
1359 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001360 if OPTIONS.no_signing:
1361 output_file = prelim_signing
1362 else:
1363 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001364
1365 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001366 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001367 for property_files in needed_property_files:
1368 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001369
1370
Tao Bao491d7e22018-02-21 13:17:22 -08001371def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001372 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1373 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001374
Tao Bao481bab82017-12-21 11:23:09 -08001375 target_api_version = target_info["recovery_api_version"]
1376 source_api_version = source_info["recovery_api_version"]
1377 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001378 logger.warning(
1379 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001380
Tao Bao481bab82017-12-21 11:23:09 -08001381 script = edify_generator.EdifyGenerator(
1382 source_api_version, target_info, fstab=source_info["fstab"])
1383
1384 if target_info.oem_props or source_info.oem_props:
1385 if not OPTIONS.oem_no_mount:
1386 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001387
Tao Baodf3a48b2018-01-10 16:30:43 -08001388 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001389
Tao Bao491d7e22018-02-21 13:17:22 -08001390 if not OPTIONS.no_signing:
1391 staging_file = common.MakeTempFile(suffix='.zip')
1392 else:
1393 staging_file = output_file
1394
1395 output_zip = zipfile.ZipFile(
1396 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1397
Geremy Condra36bd3652014-02-06 19:45:10 -08001398 device_specific = common.DeviceSpecificParams(
1399 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001400 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001401 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001402 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001403 output_zip=output_zip,
1404 script=script,
1405 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001406 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001407
Geremy Condra36bd3652014-02-06 19:45:10 -08001408 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001409 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001410 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001411 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001412 updating_boot = (not OPTIONS.two_step and
1413 (source_boot.data != target_boot.data))
1414
Geremy Condra36bd3652014-02-06 19:45:10 -08001415 target_recovery = common.GetBootableImage(
1416 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001417
Tao Baoe709b092018-02-07 12:40:00 -08001418 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1419 # shared blocks (i.e. some blocks will show up in multiple files' block
1420 # list). We can only allocate such shared blocks to the first "owner", and
1421 # disable imgdiff for all later occurrences.
1422 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1423 target_info.get('ext4_share_dup_blocks') == "true")
1424 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1425 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001426
1427 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1428 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001429 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001430 allow_shared_blocks,
1431 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001432
Tao Bao0582cb62017-12-21 11:47:01 -08001433 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001434 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001435 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001436
Tao Baof8acad12016-07-07 09:09:58 -07001437 # Check the first block of the source system partition for remount R/W only
1438 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001439 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001440 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001441 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1442 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1443 # b) the blocks listed in block map may not contain all the bytes for a given
1444 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001445 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001446 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1447 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001448 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001449 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001450 version=blockimgdiff_version,
1451 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001452
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001453 if HasVendorPartition(target_zip):
1454 if not HasVendorPartition(source_zip):
1455 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001456 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1457 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001458 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1459 "vendor", 4096, target_info)
1460 vendor_tgt = common.GetSparseImage(
1461 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1462 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001463
1464 # Check first block of vendor partition for remount R/W only if
1465 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001466 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001467 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001468 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001469 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001470 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001471 version=blockimgdiff_version,
1472 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001473 else:
1474 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001475
Tao Baobcd1d162017-08-26 13:10:26 -07001476 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001477 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001478
Tao Bao481bab82017-12-21 11:23:09 -08001479 # Assertions (e.g. device properties check).
1480 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001481 device_specific.IncrementalOTA_Assertions()
1482
1483 # Two-step incremental package strategy (in chronological order,
1484 # which is *not* the order in which the generated script has
1485 # things):
1486 #
1487 # if stage is not "2/3" or "3/3":
1488 # do verification on current system
1489 # write recovery image to boot partition
1490 # set stage to "2/3"
1491 # reboot to boot partition and restart recovery
1492 # else if stage is "2/3":
1493 # write recovery image to recovery partition
1494 # set stage to "3/3"
1495 # reboot to recovery partition and restart recovery
1496 # else:
1497 # (stage must be "3/3")
1498 # perform update:
1499 # patch system files, etc.
1500 # force full install of new boot image
1501 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001502 # complete script normally
1503 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001504
1505 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001506 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001507 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001508 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001509 assert fs.fs_type.upper() == "EMMC", \
1510 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001511 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001512 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1513 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001514if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001515""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001516
1517 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1518 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001519 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001520 script.WriteRawImage("/recovery", "recovery.img")
1521 script.AppendExtra("""
1522set_stage("%(bcb_dev)s", "3/3");
1523reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001524else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001525""" % bcb_dev)
1526
Tao Baod42e97e2016-11-30 12:11:57 -08001527 # Stage 1/3: (a) Verify the current system.
1528 script.Comment("Stage 1/3")
1529
Tao Bao6c55a8a2015-04-08 15:30:27 -07001530 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001531 script.Print("Source: {}".format(source_info.fingerprint))
1532 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001533
Geremy Condra36bd3652014-02-06 19:45:10 -08001534 script.Print("Verifying current system...")
1535
1536 device_specific.IncrementalOTA_VerifyBegin()
1537
Tao Bao481bab82017-12-21 11:23:09 -08001538 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001539
Tao Baod8d14be2016-02-04 14:26:02 -08001540 # Check the required cache size (i.e. stashed blocks).
1541 size = []
1542 if system_diff:
1543 size.append(system_diff.required_cache)
1544 if vendor_diff:
1545 size.append(vendor_diff.required_cache)
1546
Geremy Condra36bd3652014-02-06 19:45:10 -08001547 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001548 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001549 d = common.Difference(target_boot, source_boot)
1550 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001551 if d is None:
1552 include_full_boot = True
1553 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1554 else:
1555 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001556
Tao Bao32fcdab2018-10-12 10:30:39 -07001557 logger.info(
1558 "boot target: %d source: %d diff: %d", target_boot.size,
1559 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001560
Tao Bao51216552018-08-26 11:53:15 -07001561 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001562
Tao Bao51216552018-08-26 11:53:15 -07001563 script.PatchPartitionCheck(
1564 "{}:{}:{}:{}".format(
1565 boot_type, boot_device, target_boot.size, target_boot.sha1),
1566 "{}:{}:{}:{}".format(
1567 boot_type, boot_device, source_boot.size, source_boot.sha1))
1568
Tao Baod8d14be2016-02-04 14:26:02 -08001569 size.append(target_boot.size)
1570
1571 if size:
1572 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001573
1574 device_specific.IncrementalOTA_VerifyEnd()
1575
1576 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001577 # Stage 1/3: (b) Write recovery image to /boot.
1578 _WriteRecoveryImageToBoot(script, output_zip)
1579
Geremy Condra36bd3652014-02-06 19:45:10 -08001580 script.AppendExtra("""
1581set_stage("%(bcb_dev)s", "2/3");
1582reboot_now("%(bcb_dev)s", "");
1583else
1584""" % bcb_dev)
1585
Tao Baod42e97e2016-11-30 12:11:57 -08001586 # Stage 3/3: Make changes.
1587 script.Comment("Stage 3/3")
1588
Jesse Zhao75bcea02015-01-06 10:59:53 -08001589 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001590 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001591 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001592 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001593 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1594 if device_specific_diffs:
1595 assert all(isinstance(diff, common.BlockDifference)
1596 for diff in device_specific_diffs), \
1597 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1598 "BlockDifference objects"
1599 for diff in device_specific_diffs:
1600 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001601
Geremy Condra36bd3652014-02-06 19:45:10 -08001602 script.Comment("---- start making changes here ----")
1603
1604 device_specific.IncrementalOTA_InstallBegin()
1605
Yifan Hong10c530d2018-12-27 17:34:18 -08001606 block_diffs = [system_diff]
1607 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001608 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001609 block_diffs.append(vendor_diff)
1610 progress_dict["vendor"] = 0.1
1611 if device_specific_diffs:
1612 block_diffs += device_specific_diffs
1613
1614 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1615 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1616 raise RuntimeError(
1617 "can't generate incremental that disables dynamic partitions")
1618 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1619 info_dict=OPTIONS.target_info_dict,
1620 source_info_dict=OPTIONS.source_info_dict,
1621 block_diffs=block_diffs,
1622 progress_dict=progress_dict)
1623 dynamic_partitions_diff.WriteScript(
1624 script, output_zip, write_verify_script=OPTIONS.verify)
1625 else:
1626 for block_diff in block_diffs:
1627 block_diff.WriteScript(script, output_zip,
1628 progress=progress_dict.get(block_diff.partition),
1629 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001630
1631 if OPTIONS.two_step:
1632 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1633 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001634 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001635
1636 if not OPTIONS.two_step:
1637 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001638 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001639 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001640 script.Print("Installing boot image...")
1641 script.WriteRawImage("/boot", "boot.img")
1642 else:
1643 # Produce the boot image by applying a patch to the current
1644 # contents of the boot partition, and write it back to the
1645 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001646 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001647 script.Print("Patching boot image...")
1648 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001649 script.PatchPartition(
1650 '{}:{}:{}:{}'.format(
1651 boot_type, boot_device, target_boot.size, target_boot.sha1),
1652 '{}:{}:{}:{}'.format(
1653 boot_type, boot_device, source_boot.size, source_boot.sha1),
1654 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001655 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001656 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001657
1658 # Do device-specific installation (eg, write radio image).
1659 device_specific.IncrementalOTA_InstallEnd()
1660
1661 if OPTIONS.extra_script is not None:
1662 script.AppendExtra(OPTIONS.extra_script)
1663
Doug Zongker922206e2014-03-04 13:16:24 -08001664 if OPTIONS.wipe_user_data:
1665 script.Print("Erasing user data...")
1666 script.FormatPartition("/data")
1667
Geremy Condra36bd3652014-02-06 19:45:10 -08001668 if OPTIONS.two_step:
1669 script.AppendExtra("""
1670set_stage("%(bcb_dev)s", "");
1671endif;
1672endif;
1673""" % bcb_dev)
1674
1675 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001676 # For downgrade OTAs, we prefer to use the update-binary in the source
1677 # build that is actually newer than the one in the target build.
1678 if OPTIONS.downgrade:
1679 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1680 else:
1681 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001682 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001683
1684 # We haven't written the metadata entry yet, which will be handled in
1685 # FinalizeMetadata().
1686 common.ZipClose(output_zip)
1687
1688 # Sign the generated zip package unless no_signing is specified.
1689 needed_property_files = (
1690 NonAbOtaPropertyFiles(),
1691 )
1692 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001693
Doug Zongker32b527d2014-03-04 10:03:02 -08001694
Tao Bao15a146a2018-02-21 16:06:59 -08001695def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001696 """Returns a target-files.zip file for generating secondary payload.
1697
1698 Although the original target-files.zip already contains secondary slot
1699 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1700 ones without _other suffix. Note that we cannot instead modify the names in
1701 META/ab_partitions.txt, because there are no matching partitions on device.
1702
1703 For the partitions that don't have secondary images, the ones for primary
1704 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1705 bootloader images in the inactive slot.
1706
1707 Args:
1708 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001709 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001710
1711 Returns:
1712 The filename of the target-files.zip for generating secondary payload.
1713 """
1714 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1715 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1716
Tao Baodba59ee2018-01-09 13:21:02 -08001717 with zipfile.ZipFile(input_file, 'r') as input_zip:
1718 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001719 namelist = input_zip.namelist()
1720
1721 # Additionally unzip 'RADIO/*' if exists.
1722 unzip_pattern = UNZIP_PATTERN[:]
1723 if any([entry.startswith('RADIO/') for entry in namelist]):
1724 unzip_pattern.append('RADIO/*')
1725 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001726
1727 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001728 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1729 if info.filename == 'IMAGES/system_other.img':
1730 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1731
1732 # Primary images and friends need to be skipped explicitly.
1733 elif info.filename in ('IMAGES/system.img',
1734 'IMAGES/system.map'):
1735 pass
1736
Tao Bao15a146a2018-02-21 16:06:59 -08001737 # Skip copying the postinstall config if requested.
1738 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1739 pass
1740
Tao Bao12489802018-07-12 14:47:38 -07001741 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001742 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1743
Tao Baof7140c02018-01-30 17:09:24 -08001744 common.ZipClose(target_zip)
1745
1746 return target_file
1747
1748
Tao Bao15a146a2018-02-21 16:06:59 -08001749def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1750 """Returns a target-files.zip that's not containing postinstall_config.txt.
1751
1752 This allows brillo_update_payload script to skip writing all the postinstall
1753 hooks in the generated payload. The input target-files.zip file will be
1754 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1755 contain the postinstall_config.txt entry, the input file will be returned.
1756
1757 Args:
1758 input_file: The input target-files.zip filename.
1759
1760 Returns:
1761 The filename of target-files.zip that doesn't contain postinstall config.
1762 """
1763 # We should only make a copy if postinstall_config entry exists.
1764 with zipfile.ZipFile(input_file, 'r') as input_zip:
1765 if POSTINSTALL_CONFIG not in input_zip.namelist():
1766 return input_file
1767
1768 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1769 shutil.copyfile(input_file, target_file)
1770 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1771 return target_file
1772
1773
Yifan Hong50e79542018-11-08 17:44:12 -08001774def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
1775 super_block_devices):
1776 """Returns a target-files.zip for retrofitting dynamic partitions.
1777
1778 This allows brillo_update_payload to generate an OTA based on the exact
1779 bits on the block devices. Postinstall is disabled.
1780
1781 Args:
1782 input_file: The input target-files.zip filename.
1783 super_block_devices: The list of super block devices
1784
1785 Returns:
1786 The filename of target-files.zip with *.img replaced with super_*.img for
1787 each block device in super_block_devices.
1788 """
1789 assert super_block_devices, "No super_block_devices are specified."
1790
1791 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001792 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001793
1794 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1795 shutil.copyfile(input_file, target_file)
1796
1797 with zipfile.ZipFile(input_file, 'r') as input_zip:
1798 namelist = input_zip.namelist()
1799
1800 # Always skip postinstall for a retrofit update.
1801 to_delete = [POSTINSTALL_CONFIG]
1802
1803 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1804 # is a regular update on devices without dynamic partitions support.
1805 to_delete += [DYNAMIC_PARTITION_INFO]
1806
Tao Bao03fecb62018-11-28 10:59:23 -08001807 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001808 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001809 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001810
1811 common.ZipDelete(target_file, to_delete)
1812
1813 input_tmp = common.UnzipTemp(input_file, SUPER_SPLIT_PATTERN)
1814 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1815
1816 # Write super_{foo}.img as {foo}.img.
1817 for src, dst in replace.items():
1818 assert src in namelist, \
1819 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1820 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1821 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1822
1823 common.ZipClose(target_zip)
1824
1825 return target_file
1826
1827
Tao Baoc098e9e2016-01-07 13:03:56 -08001828def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1829 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001830 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001831 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001832 if not OPTIONS.no_signing:
1833 staging_file = common.MakeTempFile(suffix='.zip')
1834 else:
1835 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001836 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001837 compression=zipfile.ZIP_DEFLATED)
1838
Tao Bao481bab82017-12-21 11:23:09 -08001839 if source_file is not None:
1840 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1841 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1842 else:
1843 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1844 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001845
Tao Bao481bab82017-12-21 11:23:09 -08001846 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001847 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001848
Yifan Hong50e79542018-11-08 17:44:12 -08001849 if OPTIONS.retrofit_dynamic_partitions:
1850 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
1851 target_file, target_info.get("super_block_devices").strip().split())
1852 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001853 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1854
Tao Bao40b18822018-01-30 18:19:04 -08001855 # Generate payload.
1856 payload = Payload()
1857
1858 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001859 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001860 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001861 else:
1862 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001863 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001864
Tao Bao40b18822018-01-30 18:19:04 -08001865 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001866
Tao Bao40b18822018-01-30 18:19:04 -08001867 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001868 payload_signer = PayloadSigner()
1869 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001870
Tao Bao40b18822018-01-30 18:19:04 -08001871 # Write the payload into output zip.
1872 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001873
Tao Baof7140c02018-01-30 17:09:24 -08001874 # Generate and include the secondary payload that installs secondary images
1875 # (e.g. system_other.img).
1876 if OPTIONS.include_secondary:
1877 # We always include a full payload for the secondary slot, even when
1878 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001879 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1880 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001881 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001882 secondary_payload.Generate(secondary_target_file,
1883 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001884 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001885 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001886
Tianjie Xucfa86222016-03-07 16:31:19 -08001887 # If dm-verity is supported for the device, copy contents of care_map
1888 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001889 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001890 if (target_info.get("verity") == "true" or
1891 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001892 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1893 "META/" + x in target_zip.namelist()]
1894
1895 # Adds care_map if either the protobuf format or the plain text one exists.
1896 if care_map_list:
1897 care_map_name = care_map_list[0]
1898 care_map_data = target_zip.read("META/" + care_map_name)
1899 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001900 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001901 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001902 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001903 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001904 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001905
Tao Baobcd1d162017-08-26 13:10:26 -07001906 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001907 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001908
Tao Bao21803d32017-04-19 10:16:09 -07001909 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001910
Tao Baofe5b69a2018-03-02 09:47:43 -08001911 # We haven't written the metadata entry yet, which will be handled in
1912 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001913 common.ZipClose(output_zip)
1914
Tao Bao85f16982018-03-08 16:28:33 -08001915 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1916 # all the info of the latter. However, system updaters and OTA servers need to
1917 # take time to switch to the new flag. We keep both of the flags for
1918 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001919 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001920 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001921 StreamingPropertyFiles(),
1922 )
1923 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001924
Tao Baoc098e9e2016-01-07 13:03:56 -08001925
Doug Zongkereef39442009-04-02 12:14:19 -07001926def main(argv):
1927
1928 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001929 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001930 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001931 elif o in ("-i", "--incremental_from"):
1932 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001933 elif o == "--full_radio":
1934 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001935 elif o == "--full_bootloader":
1936 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001937 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001938 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001939 elif o == "--downgrade":
1940 OPTIONS.downgrade = True
1941 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001942 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001943 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001944 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001945 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001946 elif o == "--oem_no_mount":
1947 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001948 elif o in ("-e", "--extra_script"):
1949 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001950 elif o in ("-t", "--worker_threads"):
1951 if a.isdigit():
1952 OPTIONS.worker_threads = int(a)
1953 else:
1954 raise ValueError("Cannot parse value %r for option %r - only "
1955 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001956 elif o in ("-2", "--two_step"):
1957 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001958 elif o == "--include_secondary":
1959 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001960 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001961 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001962 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001963 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001964 elif o == "--block":
1965 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001966 elif o in ("-b", "--binary"):
1967 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001968 elif o == "--stash_threshold":
1969 try:
1970 OPTIONS.stash_threshold = float(a)
1971 except ValueError:
1972 raise ValueError("Cannot parse value %r for option %r - expecting "
1973 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001974 elif o == "--log_diff":
1975 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001976 elif o == "--payload_signer":
1977 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001978 elif o == "--payload_signer_args":
1979 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001980 elif o == "--extracted_input_target_files":
1981 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001982 elif o == "--skip_postinstall":
1983 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08001984 elif o == "--retrofit_dynamic_partitions":
1985 OPTIONS.retrofit_dynamic_partitions = True
Doug Zongkereef39442009-04-02 12:14:19 -07001986 else:
1987 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001988 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001989
1990 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001991 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001992 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001993 "package_key=",
1994 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001995 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001996 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001997 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001998 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001999 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002000 "extra_script=",
2001 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002002 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002003 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002004 "no_signing",
2005 "block",
2006 "binary=",
2007 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002008 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002009 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002010 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002011 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002012 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002013 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002014 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002015 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002016 "retrofit_dynamic_partitions",
Dan Albert8b72aef2015-03-23 19:13:21 -07002017 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002018
2019 if len(args) != 2:
2020 common.Usage(__doc__)
2021 sys.exit(1)
2022
Tao Bao32fcdab2018-10-12 10:30:39 -07002023 common.InitLogging()
2024
Tao Bao5d182562016-02-23 11:38:39 -08002025 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002026 # We should only allow downgrading incrementals (as opposed to full).
2027 # Otherwise the device may go back from arbitrary build with this full
2028 # OTA package.
2029 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002030 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002031
Tao Bao2db13852018-01-08 22:28:57 -08002032 # Load the build info dicts from the zip directly or the extracted input
2033 # directory. We don't need to unzip the entire target-files zips, because they
2034 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2035 # When loading the info dicts, we don't need to provide the second parameter
2036 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2037 # some properties with their actual paths, such as 'selinux_fc',
2038 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002039 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002040 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002041 else:
Tao Bao2db13852018-01-08 22:28:57 -08002042 with zipfile.ZipFile(args[0], 'r') as input_zip:
2043 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002044
Tao Bao32fcdab2018-10-12 10:30:39 -07002045 logger.info("--- target info ---")
2046 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002047
2048 # Load the source build dict if applicable.
2049 if OPTIONS.incremental_source is not None:
2050 OPTIONS.target_info_dict = OPTIONS.info_dict
2051 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2052 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2053
Tao Bao32fcdab2018-10-12 10:30:39 -07002054 logger.info("--- source info ---")
2055 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002056
2057 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002058 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2059
Yifan Hong50e79542018-11-08 17:44:12 -08002060 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002061 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002062 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002063 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2064 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002065 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2066 raise common.ExternalError(
2067 "Expect to generate incremental OTA for retrofitting dynamic "
2068 "partitions, but dynamic_partition_retrofit is not set in target "
2069 "build.")
2070 logger.info("Implicitly generating retrofit incremental OTA.")
2071 OPTIONS.retrofit_dynamic_partitions = True
2072
2073 # Skip postinstall for retrofitting dynamic partitions.
2074 if OPTIONS.retrofit_dynamic_partitions:
2075 OPTIONS.skip_postinstall = True
2076
Tao Baoc098e9e2016-01-07 13:03:56 -08002077 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2078
Christian Oderf63e2cd2017-05-01 22:30:15 +02002079 # Use the default key to sign the package if not specified with package_key.
2080 # package_keys are needed on ab_updates, so always define them if an
2081 # ab_update is getting created.
2082 if not OPTIONS.no_signing or ab_update:
2083 if OPTIONS.package_key is None:
2084 OPTIONS.package_key = OPTIONS.info_dict.get(
2085 "default_system_dev_certificate",
2086 "build/target/product/security/testkey")
2087 # Get signing keys
2088 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2089
Tao Baoc098e9e2016-01-07 13:03:56 -08002090 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002091 WriteABOTAPackageWithBrilloScript(
2092 target_file=args[0],
2093 output_file=args[1],
2094 source_file=OPTIONS.incremental_source)
2095
Tao Bao32fcdab2018-10-12 10:30:39 -07002096 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002097 return
2098
Tao Bao2db13852018-01-08 22:28:57 -08002099 # Sanity check the loaded info dicts first.
2100 if OPTIONS.info_dict.get("no_recovery") == "true":
2101 raise common.ExternalError(
2102 "--- target build has specified no recovery ---")
2103
2104 # Non-A/B OTAs rely on /cache partition to store temporary files.
2105 cache_size = OPTIONS.info_dict.get("cache_size")
2106 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002107 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002108 OPTIONS.cache_size = cache_size
2109
Doug Zongker1c390a22009-05-14 19:06:36 -07002110 if OPTIONS.extra_script is not None:
2111 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2112
Dan Willemsencea5cd22017-03-21 14:44:27 -07002113 if OPTIONS.extracted_input is not None:
2114 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002115 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002116 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002117 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002118 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002119
Tao Bao2db13852018-01-08 22:28:57 -08002120 # If the caller explicitly specified the device-specific extensions path via
2121 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2122 # is present in the target target_files. Otherwise, take the path of the file
2123 # from 'tool_extensions' in the info dict and look for that in the local
2124 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002125 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002126 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2127 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002128 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002129 OPTIONS.device_specific = from_input
2130 else:
Tao Bao2db13852018-01-08 22:28:57 -08002131 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002132
Doug Zongker37974732010-09-16 17:44:38 -07002133 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002134 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002135
Tao Bao767e3ac2015-11-10 12:19:19 -08002136 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002137 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002138 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002139 WriteFullOTAPackage(
2140 input_zip,
2141 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002142
Tao Bao32b80dc2018-01-08 22:50:47 -08002143 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002144 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002145 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002146 OPTIONS.source_tmp = common.UnzipTemp(
2147 OPTIONS.incremental_source, UNZIP_PATTERN)
2148 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2149 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002150 WriteBlockIncrementalOTAPackage(
2151 input_zip,
2152 source_zip,
2153 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002154
2155 if OPTIONS.log_diff:
2156 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002157 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002158 target_files_diff.recursiveDiff(
2159 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002160
Tao Bao32fcdab2018-10-12 10:30:39 -07002161 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002162
2163
2164if __name__ == '__main__':
2165 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002166 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002167 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002168 except common.ExternalError:
2169 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002170 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002171 finally:
2172 common.Cleanup()