blob: 7ec8ad82ee316970cf13ba3fa50e5a03e4b82dd9 [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
Tao Bao457cbf62017-03-06 09:56:01 -0800829 # Full OTA is done as an "incremental" against an empty source image. This
830 # has the effect of writing new data from the package to the entire
831 # partition, but lets us reuse the updater code that writes incrementals to
832 # do it.
Tao Baoe709b092018-02-07 12:40:00 -0800833 system_tgt = common.GetSparseImage("system", OPTIONS.input_tmp, input_zip,
834 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800835 system_tgt.ResetFileMap()
836 system_diff = common.BlockDifference("system", system_tgt, src=None)
Tao Bao76def242017-11-21 09:25:31 -0800837 system_diff.WriteScript(script, output_zip,
838 write_verify_script=OPTIONS.verify)
Doug Zongkereef39442009-04-02 12:14:19 -0700839
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700840 boot_img = common.GetBootableImage(
841 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800842
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700843 if HasVendorPartition(input_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700844 script.ShowProgress(0.1, 0)
845
Tao Baoe709b092018-02-07 12:40:00 -0800846 vendor_tgt = common.GetSparseImage("vendor", OPTIONS.input_tmp, input_zip,
847 allow_shared_blocks)
Tao Bao457cbf62017-03-06 09:56:01 -0800848 vendor_tgt.ResetFileMap()
849 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Tao Bao76def242017-11-21 09:25:31 -0800850 vendor_diff.WriteScript(script, output_zip,
851 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700852
Tao Bao481bab82017-12-21 11:23:09 -0800853 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700854
Tao Bao481bab82017-12-21 11:23:09 -0800855 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700856 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700857
Doug Zongker01ce19c2014-02-04 13:48:15 -0800858 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700859 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700860
Doug Zongker01ce19c2014-02-04 13:48:15 -0800861 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700862 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700863
Doug Zongker1c390a22009-05-14 19:06:36 -0700864 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700865 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700866
Doug Zongker14833602010-02-02 13:12:04 -0800867 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800868
Doug Zongker922206e2014-03-04 13:16:24 -0800869 if OPTIONS.wipe_user_data:
870 script.ShowProgress(0.1, 10)
871 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700872
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800873 if OPTIONS.two_step:
874 script.AppendExtra("""
875set_stage("%(bcb_dev)s", "");
876""" % bcb_dev)
877 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800878
879 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
880 script.Comment("Stage 1/3")
881 _WriteRecoveryImageToBoot(script, output_zip)
882
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800883 script.AppendExtra("""
884set_stage("%(bcb_dev)s", "2/3");
885reboot_now("%(bcb_dev)s", "");
886endif;
887endif;
888""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800889
Tao Bao5d182562016-02-23 11:38:39 -0800890 script.SetProgress(1)
891 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800892 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800893
894 # We haven't written the metadata entry, which will be done in
895 # FinalizeMetadata.
896 common.ZipClose(output_zip)
897
898 needed_property_files = (
899 NonAbOtaPropertyFiles(),
900 )
901 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700902
Doug Zongkerfc44a512014-08-26 13:10:25 -0700903
Doug Zongker2ea21062010-04-28 16:05:21 -0700904def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800905 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
906 common.ZipWriteStr(output_zip, METADATA_NAME, value,
907 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700908
Doug Zongkerfc44a512014-08-26 13:10:25 -0700909
Tao Bao481bab82017-12-21 11:23:09 -0800910def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800911 # Only incremental OTAs are allowed to reach here.
912 assert OPTIONS.incremental_source is not None
913
Tao Bao481bab82017-12-21 11:23:09 -0800914 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
915 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800916 is_downgrade = long(post_timestamp) < long(pre_timestamp)
917
918 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800919 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700920 raise RuntimeError(
921 "--downgrade or --override_timestamp specified but no downgrade "
922 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800923 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800924 else:
925 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700926 raise RuntimeError(
927 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
928 "Need to specify --override_timestamp OR --downgrade to allow "
929 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800930
931
Tao Baodf3a48b2018-01-10 16:30:43 -0800932def GetPackageMetadata(target_info, source_info=None):
933 """Generates and returns the metadata dict.
934
935 It generates a dict() that contains the info to be written into an OTA
936 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -0700937 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -0800938
939 Args:
940 target_info: The BuildInfo instance that holds the target build info.
941 source_info: The BuildInfo instance that holds the source build info, or
942 None if generating full OTA.
943
944 Returns:
945 A dict to be written into package metadata entry.
946 """
947 assert isinstance(target_info, BuildInfo)
948 assert source_info is None or isinstance(source_info, BuildInfo)
949
950 metadata = {
951 'post-build' : target_info.fingerprint,
952 'post-build-incremental' : target_info.GetBuildProp(
953 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -0800954 'post-sdk-level' : target_info.GetBuildProp(
955 'ro.build.version.sdk'),
956 'post-security-patch-level' : target_info.GetBuildProp(
957 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -0800958 }
959
960 if target_info.is_ab:
961 metadata['ota-type'] = 'AB'
962 metadata['ota-required-cache'] = '0'
963 else:
964 metadata['ota-type'] = 'BLOCK'
965
966 if OPTIONS.wipe_user_data:
967 metadata['ota-wipe'] = 'yes'
968
969 is_incremental = source_info is not None
970 if is_incremental:
971 metadata['pre-build'] = source_info.fingerprint
972 metadata['pre-build-incremental'] = source_info.GetBuildProp(
973 'ro.build.version.incremental')
974 metadata['pre-device'] = source_info.device
975 else:
976 metadata['pre-device'] = target_info.device
977
Tao Baofaa8e0b2018-04-12 14:31:43 -0700978 # Use the actual post-timestamp, even for a downgrade case.
979 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
980
981 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -0800982 if is_incremental:
983 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800984
985 return metadata
986
987
Tao Baod3fc38a2018-03-08 16:09:01 -0800988class PropertyFiles(object):
989 """A class that computes the property-files string for an OTA package.
990
991 A property-files string is a comma-separated string that contains the
992 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
993 can be fetched directly with the package URL along with the offset/size info.
994 These strings can be used for streaming A/B OTAs, or allowing an updater to
995 download package metadata entry directly, without paying the cost of
996 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -0800997
Tao Baocc8e2662018-03-01 19:30:00 -0800998 Computing the final property-files string requires two passes. Because doing
999 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1000 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1001 values.
1002
1003 This class provides functions to be called for each pass. The general flow is
1004 as follows.
1005
Tao Baod3fc38a2018-03-08 16:09:01 -08001006 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001007 # The first pass, which writes placeholders before doing initial signing.
1008 property_files.Compute()
1009 SignOutput()
1010
1011 # The second pass, by replacing the placeholders with actual data.
1012 property_files.Finalize()
1013 SignOutput()
1014
1015 And the caller can additionally verify the final result.
1016
1017 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001018 """
1019
Tao Baocc8e2662018-03-01 19:30:00 -08001020 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001021 self.name = None
1022 self.required = ()
1023 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001024
Tao Baocc8e2662018-03-01 19:30:00 -08001025 def Compute(self, input_zip):
1026 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001027
Tao Baocc8e2662018-03-01 19:30:00 -08001028 We reserve extra space for the offset and size of the metadata entry itself,
1029 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001030
Tao Baocc8e2662018-03-01 19:30:00 -08001031 Args:
1032 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001033
Tao Baocc8e2662018-03-01 19:30:00 -08001034 Returns:
1035 A string with placeholders for the metadata offset/size info, e.g.
1036 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1037 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001038 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001039
Tao Baod2ce2ed2018-03-16 12:59:42 -07001040 class InsufficientSpaceException(Exception):
1041 pass
1042
Tao Baocc8e2662018-03-01 19:30:00 -08001043 def Finalize(self, input_zip, reserved_length):
1044 """Finalizes a property-files string with actual METADATA offset/size info.
1045
1046 The input ZIP file has been signed, with the ZIP entries in the desired
1047 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1048 the ZIP entry offsets and construct the property-files string with actual
1049 data. Note that during this process, we must pad the property-files string
1050 to the reserved length, so that the METADATA entry size remains the same.
1051 Otherwise the entries' offsets and sizes may change again.
1052
1053 Args:
1054 input_zip: The input ZIP file.
1055 reserved_length: The reserved length of the property-files string during
1056 the call to Compute(). The final string must be no more than this
1057 size.
1058
1059 Returns:
1060 A property-files string including the metadata offset/size info, e.g.
1061 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1062
1063 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001064 InsufficientSpaceException: If the reserved length is insufficient to hold
1065 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001066 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001067 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001068 if len(result) > reserved_length:
1069 raise self.InsufficientSpaceException(
1070 'Insufficient reserved space: reserved={}, actual={}'.format(
1071 reserved_length, len(result)))
1072
Tao Baocc8e2662018-03-01 19:30:00 -08001073 result += ' ' * (reserved_length - len(result))
1074 return result
1075
1076 def Verify(self, input_zip, expected):
1077 """Verifies the input ZIP file contains the expected property-files string.
1078
1079 Args:
1080 input_zip: The input ZIP file.
1081 expected: The property-files string that's computed from Finalize().
1082
1083 Raises:
1084 AssertionError: On finding a mismatch.
1085 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001086 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001087 assert actual == expected, \
1088 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1089
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001090 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1091 """
1092 Constructs the property-files string per request.
1093
1094 Args:
1095 zip_file: The input ZIP file.
1096 reserved_length: The reserved length of the property-files string.
1097
1098 Returns:
1099 A property-files string including the metadata offset/size info, e.g.
1100 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1101 """
Tao Baocc8e2662018-03-01 19:30:00 -08001102
1103 def ComputeEntryOffsetSize(name):
1104 """Computes the zip entry offset and size."""
1105 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001106 offset = info.header_offset
1107 offset += zipfile.sizeFileHeader
1108 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001109 size = info.file_size
1110 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1111
1112 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001113 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001114 for entry in self.required:
1115 tokens.append(ComputeEntryOffsetSize(entry))
1116 for entry in self.optional:
1117 if entry in zip_file.namelist():
1118 tokens.append(ComputeEntryOffsetSize(entry))
1119
1120 # 'META-INF/com/android/metadata' is required. We don't know its actual
1121 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001122 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1123 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1124 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1125 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001126 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001127 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001128 else:
1129 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1130
1131 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001132
Tao Bao85f16982018-03-08 16:28:33 -08001133 def _GetPrecomputed(self, input_zip):
1134 """Computes the additional tokens to be included into the property-files.
1135
1136 This applies to tokens without actual ZIP entries, such as
1137 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1138 that they can download the payload metadata directly with the info.
1139
1140 Args:
1141 input_zip: The input zip file.
1142
1143 Returns:
1144 A list of strings (tokens) to be added to the property-files string.
1145 """
1146 # pylint: disable=no-self-use
1147 # pylint: disable=unused-argument
1148 return []
1149
Tao Baofe5b69a2018-03-02 09:47:43 -08001150
Tao Baod3fc38a2018-03-08 16:09:01 -08001151class StreamingPropertyFiles(PropertyFiles):
1152 """A subclass for computing the property-files for streaming A/B OTAs."""
1153
1154 def __init__(self):
1155 super(StreamingPropertyFiles, self).__init__()
1156 self.name = 'ota-streaming-property-files'
1157 self.required = (
1158 # payload.bin and payload_properties.txt must exist.
1159 'payload.bin',
1160 'payload_properties.txt',
1161 )
1162 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001163 # care_map is available only if dm-verity is enabled.
1164 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001165 'care_map.txt',
1166 # compatibility.zip is available only if target supports Treble.
1167 'compatibility.zip',
1168 )
1169
1170
Tao Bao85f16982018-03-08 16:28:33 -08001171class AbOtaPropertyFiles(StreamingPropertyFiles):
1172 """The property-files for A/B OTA that includes payload_metadata.bin info.
1173
1174 Since P, we expose one more token (aka property-file), in addition to the ones
1175 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1176 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1177 doesn't exist as a separate ZIP entry, but can be used to verify if the
1178 payload can be applied on the given device.
1179
1180 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1181 and the newly added 'ota-property-files' in P. The new token will only be
1182 available in 'ota-property-files'.
1183 """
1184
1185 def __init__(self):
1186 super(AbOtaPropertyFiles, self).__init__()
1187 self.name = 'ota-property-files'
1188
1189 def _GetPrecomputed(self, input_zip):
1190 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1191 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1192
1193 @staticmethod
1194 def _GetPayloadMetadataOffsetAndSize(input_zip):
1195 """Computes the offset and size of the payload metadata for a given package.
1196
1197 (From system/update_engine/update_metadata.proto)
1198 A delta update file contains all the deltas needed to update a system from
1199 one specific version to another specific version. The update format is
1200 represented by this struct pseudocode:
1201
1202 struct delta_update_file {
1203 char magic[4] = "CrAU";
1204 uint64 file_format_version;
1205 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1206
1207 // Only present if format_version > 1:
1208 uint32 metadata_signature_size;
1209
1210 // The Bzip2 compressed DeltaArchiveManifest
1211 char manifest[metadata_signature_size];
1212
1213 // The signature of the metadata (from the beginning of the payload up to
1214 // this location, not including the signature itself). This is a
1215 // serialized Signatures message.
1216 char medatada_signature_message[metadata_signature_size];
1217
1218 // Data blobs for files, no specific format. The specific offset
1219 // and length of each data blob is recorded in the DeltaArchiveManifest.
1220 struct {
1221 char data[];
1222 } blobs[];
1223
1224 // These two are not signed:
1225 uint64 payload_signatures_message_size;
1226 char payload_signatures_message[];
1227 };
1228
1229 'payload-metadata.bin' contains all the bytes from the beginning of the
1230 payload, till the end of 'medatada_signature_message'.
1231 """
1232 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001233 payload_offset = payload_info.header_offset
1234 payload_offset += zipfile.sizeFileHeader
1235 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001236 payload_size = payload_info.file_size
1237
1238 with input_zip.open('payload.bin', 'r') as payload_fp:
1239 header_bin = payload_fp.read(24)
1240
1241 # network byte order (big-endian)
1242 header = struct.unpack("!IQQL", header_bin)
1243
1244 # 'CrAU'
1245 magic = header[0]
1246 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1247
1248 manifest_size = header[2]
1249 metadata_signature_size = header[3]
1250 metadata_total = 24 + manifest_size + metadata_signature_size
1251 assert metadata_total < payload_size
1252
1253 return (payload_offset, metadata_total)
1254
1255
Tao Bao491d7e22018-02-21 13:17:22 -08001256class NonAbOtaPropertyFiles(PropertyFiles):
1257 """The property-files for non-A/B OTA.
1258
1259 For non-A/B OTA, the property-files string contains the info for METADATA
1260 entry, with which a system updater can be fetched the package metadata prior
1261 to downloading the entire package.
1262 """
1263
1264 def __init__(self):
1265 super(NonAbOtaPropertyFiles, self).__init__()
1266 self.name = 'ota-property-files'
1267
1268
Tao Baod3fc38a2018-03-08 16:09:01 -08001269def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001270 """Finalizes the metadata and signs an A/B OTA package.
1271
1272 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1273 that contains the offsets and sizes for the ZIP entries. An example
1274 property-files string is as follows.
1275
1276 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1277
1278 OTA server can pass down this string, in addition to the package URL, to the
1279 system update client. System update client can then fetch individual ZIP
1280 entries (ZIP_STORED) directly at the given offset of the URL.
1281
1282 Args:
1283 metadata: The metadata dict for the package.
1284 input_file: The input ZIP filename that doesn't contain the package METADATA
1285 entry yet.
1286 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001287 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001288 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001289
Tao Baod2ce2ed2018-03-16 12:59:42 -07001290 def ComputeAllPropertyFiles(input_file, needed_property_files):
1291 # Write the current metadata entry with placeholders.
1292 with zipfile.ZipFile(input_file) as input_zip:
1293 for property_files in needed_property_files:
1294 metadata[property_files.name] = property_files.Compute(input_zip)
1295 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001296
Tao Baod2ce2ed2018-03-16 12:59:42 -07001297 if METADATA_NAME in namelist:
1298 common.ZipDelete(input_file, METADATA_NAME)
1299 output_zip = zipfile.ZipFile(input_file, 'a')
1300 WriteMetadata(metadata, output_zip)
1301 common.ZipClose(output_zip)
1302
1303 if OPTIONS.no_signing:
1304 return input_file
1305
Tao Bao491d7e22018-02-21 13:17:22 -08001306 prelim_signing = common.MakeTempFile(suffix='.zip')
1307 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001308 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001309
Tao Baod2ce2ed2018-03-16 12:59:42 -07001310 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1311 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1312 for property_files in needed_property_files:
1313 metadata[property_files.name] = property_files.Finalize(
1314 prelim_signing_zip, len(metadata[property_files.name]))
1315
1316 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1317 # entries, as well as padding the entry headers. We do a preliminary signing
1318 # (with an incomplete metadata entry) to allow that to happen. Then compute
1319 # the ZIP entry offsets, write back the final metadata and do the final
1320 # signing.
1321 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1322 try:
1323 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1324 except PropertyFiles.InsufficientSpaceException:
1325 # Even with the preliminary signing, the entry orders may change
1326 # dramatically, which leads to insufficiently reserved space during the
1327 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1328 # preliminary signing works, based on the already ordered ZIP entries, to
1329 # address the issue.
1330 prelim_signing = ComputeAllPropertyFiles(
1331 prelim_signing, needed_property_files)
1332 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001333
1334 # Replace the METADATA entry.
1335 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001336 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001337 WriteMetadata(metadata, output_zip)
1338 common.ZipClose(output_zip)
1339
1340 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001341 if OPTIONS.no_signing:
1342 output_file = prelim_signing
1343 else:
1344 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001345
1346 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001347 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001348 for property_files in needed_property_files:
1349 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001350
1351
Tao Bao491d7e22018-02-21 13:17:22 -08001352def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001353 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1354 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001355
Tao Bao481bab82017-12-21 11:23:09 -08001356 target_api_version = target_info["recovery_api_version"]
1357 source_api_version = source_info["recovery_api_version"]
1358 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001359 logger.warning(
1360 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001361
Tao Bao481bab82017-12-21 11:23:09 -08001362 script = edify_generator.EdifyGenerator(
1363 source_api_version, target_info, fstab=source_info["fstab"])
1364
1365 if target_info.oem_props or source_info.oem_props:
1366 if not OPTIONS.oem_no_mount:
1367 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001368
Tao Baodf3a48b2018-01-10 16:30:43 -08001369 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001370
Tao Bao491d7e22018-02-21 13:17:22 -08001371 if not OPTIONS.no_signing:
1372 staging_file = common.MakeTempFile(suffix='.zip')
1373 else:
1374 staging_file = output_file
1375
1376 output_zip = zipfile.ZipFile(
1377 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1378
Geremy Condra36bd3652014-02-06 19:45:10 -08001379 device_specific = common.DeviceSpecificParams(
1380 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001381 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001382 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001383 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001384 output_zip=output_zip,
1385 script=script,
1386 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001387 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001388
Geremy Condra36bd3652014-02-06 19:45:10 -08001389 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001390 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001391 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001392 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001393 updating_boot = (not OPTIONS.two_step and
1394 (source_boot.data != target_boot.data))
1395
Geremy Condra36bd3652014-02-06 19:45:10 -08001396 target_recovery = common.GetBootableImage(
1397 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001398
Tao Baoe709b092018-02-07 12:40:00 -08001399 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1400 # shared blocks (i.e. some blocks will show up in multiple files' block
1401 # list). We can only allocate such shared blocks to the first "owner", and
1402 # disable imgdiff for all later occurrences.
1403 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1404 target_info.get('ext4_share_dup_blocks') == "true")
1405 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1406 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001407
1408 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1409 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001410 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001411 allow_shared_blocks,
1412 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001413
Tao Bao0582cb62017-12-21 11:47:01 -08001414 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001415 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001416 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001417
Tao Baof8acad12016-07-07 09:09:58 -07001418 # Check the first block of the source system partition for remount R/W only
1419 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001420 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001421 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001422 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1423 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1424 # b) the blocks listed in block map may not contain all the bytes for a given
1425 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001426 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001427 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1428 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001429 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001430 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001431 version=blockimgdiff_version,
1432 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001433
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001434 if HasVendorPartition(target_zip):
1435 if not HasVendorPartition(source_zip):
1436 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001437 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1438 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001439 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1440 "vendor", 4096, target_info)
1441 vendor_tgt = common.GetSparseImage(
1442 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1443 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001444
1445 # Check first block of vendor partition for remount R/W only if
1446 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001447 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001448 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001449 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001450 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001451 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001452 version=blockimgdiff_version,
1453 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001454 else:
1455 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001456
Tao Baobcd1d162017-08-26 13:10:26 -07001457 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001458 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001459
Tao Bao481bab82017-12-21 11:23:09 -08001460 # Assertions (e.g. device properties check).
1461 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001462 device_specific.IncrementalOTA_Assertions()
1463
1464 # Two-step incremental package strategy (in chronological order,
1465 # which is *not* the order in which the generated script has
1466 # things):
1467 #
1468 # if stage is not "2/3" or "3/3":
1469 # do verification on current system
1470 # write recovery image to boot partition
1471 # set stage to "2/3"
1472 # reboot to boot partition and restart recovery
1473 # else if stage is "2/3":
1474 # write recovery image to recovery partition
1475 # set stage to "3/3"
1476 # reboot to recovery partition and restart recovery
1477 # else:
1478 # (stage must be "3/3")
1479 # perform update:
1480 # patch system files, etc.
1481 # force full install of new boot image
1482 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001483 # complete script normally
1484 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001485
1486 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001487 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001488 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001489 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001490 assert fs.fs_type.upper() == "EMMC", \
1491 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001492 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001493 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1494 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001495if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001496""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001497
1498 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1499 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001500 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001501 script.WriteRawImage("/recovery", "recovery.img")
1502 script.AppendExtra("""
1503set_stage("%(bcb_dev)s", "3/3");
1504reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001505else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001506""" % bcb_dev)
1507
Tao Baod42e97e2016-11-30 12:11:57 -08001508 # Stage 1/3: (a) Verify the current system.
1509 script.Comment("Stage 1/3")
1510
Tao Bao6c55a8a2015-04-08 15:30:27 -07001511 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001512 script.Print("Source: {}".format(source_info.fingerprint))
1513 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001514
Geremy Condra36bd3652014-02-06 19:45:10 -08001515 script.Print("Verifying current system...")
1516
1517 device_specific.IncrementalOTA_VerifyBegin()
1518
Tao Bao481bab82017-12-21 11:23:09 -08001519 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001520
Tao Baod8d14be2016-02-04 14:26:02 -08001521 # Check the required cache size (i.e. stashed blocks).
1522 size = []
1523 if system_diff:
1524 size.append(system_diff.required_cache)
1525 if vendor_diff:
1526 size.append(vendor_diff.required_cache)
1527
Geremy Condra36bd3652014-02-06 19:45:10 -08001528 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001529 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001530 d = common.Difference(target_boot, source_boot)
1531 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001532 if d is None:
1533 include_full_boot = True
1534 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1535 else:
1536 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001537
Tao Bao32fcdab2018-10-12 10:30:39 -07001538 logger.info(
1539 "boot target: %d source: %d diff: %d", target_boot.size,
1540 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001541
Tao Bao51216552018-08-26 11:53:15 -07001542 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001543
Tao Bao51216552018-08-26 11:53:15 -07001544 script.PatchPartitionCheck(
1545 "{}:{}:{}:{}".format(
1546 boot_type, boot_device, target_boot.size, target_boot.sha1),
1547 "{}:{}:{}:{}".format(
1548 boot_type, boot_device, source_boot.size, source_boot.sha1))
1549
Tao Baod8d14be2016-02-04 14:26:02 -08001550 size.append(target_boot.size)
1551
1552 if size:
1553 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001554
1555 device_specific.IncrementalOTA_VerifyEnd()
1556
1557 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001558 # Stage 1/3: (b) Write recovery image to /boot.
1559 _WriteRecoveryImageToBoot(script, output_zip)
1560
Geremy Condra36bd3652014-02-06 19:45:10 -08001561 script.AppendExtra("""
1562set_stage("%(bcb_dev)s", "2/3");
1563reboot_now("%(bcb_dev)s", "");
1564else
1565""" % bcb_dev)
1566
Tao Baod42e97e2016-11-30 12:11:57 -08001567 # Stage 3/3: Make changes.
1568 script.Comment("Stage 3/3")
1569
Jesse Zhao75bcea02015-01-06 10:59:53 -08001570 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001571 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001572 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001573 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001574
Geremy Condra36bd3652014-02-06 19:45:10 -08001575 script.Comment("---- start making changes here ----")
1576
1577 device_specific.IncrementalOTA_InstallBegin()
1578
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001579 system_diff.WriteScript(script, output_zip,
Tao Bao76def242017-11-21 09:25:31 -08001580 progress=0.8 if vendor_diff else 0.9,
1581 write_verify_script=OPTIONS.verify)
Tao Bao68658c02015-06-01 13:40:49 -07001582
Doug Zongkerfc44a512014-08-26 13:10:25 -07001583 if vendor_diff:
Tao Bao76def242017-11-21 09:25:31 -08001584 vendor_diff.WriteScript(script, output_zip, progress=0.1,
1585 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001586
1587 if OPTIONS.two_step:
1588 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1589 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001590 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001591
1592 if not OPTIONS.two_step:
1593 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001594 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001595 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001596 script.Print("Installing boot image...")
1597 script.WriteRawImage("/boot", "boot.img")
1598 else:
1599 # Produce the boot image by applying a patch to the current
1600 # contents of the boot partition, and write it back to the
1601 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001602 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001603 script.Print("Patching boot image...")
1604 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001605 script.PatchPartition(
1606 '{}:{}:{}:{}'.format(
1607 boot_type, boot_device, target_boot.size, target_boot.sha1),
1608 '{}:{}:{}:{}'.format(
1609 boot_type, boot_device, source_boot.size, source_boot.sha1),
1610 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001611 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001612 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001613
1614 # Do device-specific installation (eg, write radio image).
1615 device_specific.IncrementalOTA_InstallEnd()
1616
1617 if OPTIONS.extra_script is not None:
1618 script.AppendExtra(OPTIONS.extra_script)
1619
Doug Zongker922206e2014-03-04 13:16:24 -08001620 if OPTIONS.wipe_user_data:
1621 script.Print("Erasing user data...")
1622 script.FormatPartition("/data")
1623
Geremy Condra36bd3652014-02-06 19:45:10 -08001624 if OPTIONS.two_step:
1625 script.AppendExtra("""
1626set_stage("%(bcb_dev)s", "");
1627endif;
1628endif;
1629""" % bcb_dev)
1630
1631 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001632 # For downgrade OTAs, we prefer to use the update-binary in the source
1633 # build that is actually newer than the one in the target build.
1634 if OPTIONS.downgrade:
1635 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1636 else:
1637 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001638 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001639
1640 # We haven't written the metadata entry yet, which will be handled in
1641 # FinalizeMetadata().
1642 common.ZipClose(output_zip)
1643
1644 # Sign the generated zip package unless no_signing is specified.
1645 needed_property_files = (
1646 NonAbOtaPropertyFiles(),
1647 )
1648 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001649
Doug Zongker32b527d2014-03-04 10:03:02 -08001650
Tao Bao15a146a2018-02-21 16:06:59 -08001651def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001652 """Returns a target-files.zip file for generating secondary payload.
1653
1654 Although the original target-files.zip already contains secondary slot
1655 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1656 ones without _other suffix. Note that we cannot instead modify the names in
1657 META/ab_partitions.txt, because there are no matching partitions on device.
1658
1659 For the partitions that don't have secondary images, the ones for primary
1660 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1661 bootloader images in the inactive slot.
1662
1663 Args:
1664 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001665 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001666
1667 Returns:
1668 The filename of the target-files.zip for generating secondary payload.
1669 """
1670 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1671 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1672
Tao Baodba59ee2018-01-09 13:21:02 -08001673 with zipfile.ZipFile(input_file, 'r') as input_zip:
1674 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001675 namelist = input_zip.namelist()
1676
1677 # Additionally unzip 'RADIO/*' if exists.
1678 unzip_pattern = UNZIP_PATTERN[:]
1679 if any([entry.startswith('RADIO/') for entry in namelist]):
1680 unzip_pattern.append('RADIO/*')
1681 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001682
1683 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001684 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1685 if info.filename == 'IMAGES/system_other.img':
1686 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1687
1688 # Primary images and friends need to be skipped explicitly.
1689 elif info.filename in ('IMAGES/system.img',
1690 'IMAGES/system.map'):
1691 pass
1692
Tao Bao15a146a2018-02-21 16:06:59 -08001693 # Skip copying the postinstall config if requested.
1694 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1695 pass
1696
Tao Bao12489802018-07-12 14:47:38 -07001697 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001698 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1699
Tao Baof7140c02018-01-30 17:09:24 -08001700 common.ZipClose(target_zip)
1701
1702 return target_file
1703
1704
Tao Bao15a146a2018-02-21 16:06:59 -08001705def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1706 """Returns a target-files.zip that's not containing postinstall_config.txt.
1707
1708 This allows brillo_update_payload script to skip writing all the postinstall
1709 hooks in the generated payload. The input target-files.zip file will be
1710 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1711 contain the postinstall_config.txt entry, the input file will be returned.
1712
1713 Args:
1714 input_file: The input target-files.zip filename.
1715
1716 Returns:
1717 The filename of target-files.zip that doesn't contain postinstall config.
1718 """
1719 # We should only make a copy if postinstall_config entry exists.
1720 with zipfile.ZipFile(input_file, 'r') as input_zip:
1721 if POSTINSTALL_CONFIG not in input_zip.namelist():
1722 return input_file
1723
1724 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1725 shutil.copyfile(input_file, target_file)
1726 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1727 return target_file
1728
1729
Yifan Hong50e79542018-11-08 17:44:12 -08001730def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
1731 super_block_devices):
1732 """Returns a target-files.zip for retrofitting dynamic partitions.
1733
1734 This allows brillo_update_payload to generate an OTA based on the exact
1735 bits on the block devices. Postinstall is disabled.
1736
1737 Args:
1738 input_file: The input target-files.zip filename.
1739 super_block_devices: The list of super block devices
1740
1741 Returns:
1742 The filename of target-files.zip with *.img replaced with super_*.img for
1743 each block device in super_block_devices.
1744 """
1745 assert super_block_devices, "No super_block_devices are specified."
1746
1747 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001748 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001749
1750 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1751 shutil.copyfile(input_file, target_file)
1752
1753 with zipfile.ZipFile(input_file, 'r') as input_zip:
1754 namelist = input_zip.namelist()
1755
1756 # Always skip postinstall for a retrofit update.
1757 to_delete = [POSTINSTALL_CONFIG]
1758
1759 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1760 # is a regular update on devices without dynamic partitions support.
1761 to_delete += [DYNAMIC_PARTITION_INFO]
1762
Tao Bao03fecb62018-11-28 10:59:23 -08001763 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001764 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001765 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001766
1767 common.ZipDelete(target_file, to_delete)
1768
1769 input_tmp = common.UnzipTemp(input_file, SUPER_SPLIT_PATTERN)
1770 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1771
1772 # Write super_{foo}.img as {foo}.img.
1773 for src, dst in replace.items():
1774 assert src in namelist, \
1775 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1776 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1777 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1778
1779 common.ZipClose(target_zip)
1780
1781 return target_file
1782
1783
Tao Baoc098e9e2016-01-07 13:03:56 -08001784def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1785 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001786 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001787 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001788 if not OPTIONS.no_signing:
1789 staging_file = common.MakeTempFile(suffix='.zip')
1790 else:
1791 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001792 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001793 compression=zipfile.ZIP_DEFLATED)
1794
Tao Bao481bab82017-12-21 11:23:09 -08001795 if source_file is not None:
1796 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1797 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1798 else:
1799 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1800 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001801
Tao Bao481bab82017-12-21 11:23:09 -08001802 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001803 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001804
Yifan Hong50e79542018-11-08 17:44:12 -08001805 if OPTIONS.retrofit_dynamic_partitions:
1806 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
1807 target_file, target_info.get("super_block_devices").strip().split())
1808 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001809 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1810
Tao Bao40b18822018-01-30 18:19:04 -08001811 # Generate payload.
1812 payload = Payload()
1813
1814 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001815 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001816 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001817 else:
1818 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001819 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001820
Tao Bao40b18822018-01-30 18:19:04 -08001821 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001822
Tao Bao40b18822018-01-30 18:19:04 -08001823 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001824 payload_signer = PayloadSigner()
1825 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001826
Tao Bao40b18822018-01-30 18:19:04 -08001827 # Write the payload into output zip.
1828 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001829
Tao Baof7140c02018-01-30 17:09:24 -08001830 # Generate and include the secondary payload that installs secondary images
1831 # (e.g. system_other.img).
1832 if OPTIONS.include_secondary:
1833 # We always include a full payload for the secondary slot, even when
1834 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001835 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1836 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001837 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001838 secondary_payload.Generate(secondary_target_file,
1839 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001840 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001841 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001842
Tianjie Xucfa86222016-03-07 16:31:19 -08001843 # If dm-verity is supported for the device, copy contents of care_map
1844 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001845 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001846 if (target_info.get("verity") == "true" or
1847 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001848 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1849 "META/" + x in target_zip.namelist()]
1850
1851 # Adds care_map if either the protobuf format or the plain text one exists.
1852 if care_map_list:
1853 care_map_name = care_map_list[0]
1854 care_map_data = target_zip.read("META/" + care_map_name)
1855 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001856 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001857 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001858 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001859 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001860 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001861
Tao Baobcd1d162017-08-26 13:10:26 -07001862 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001863 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001864
Tao Bao21803d32017-04-19 10:16:09 -07001865 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001866
Tao Baofe5b69a2018-03-02 09:47:43 -08001867 # We haven't written the metadata entry yet, which will be handled in
1868 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001869 common.ZipClose(output_zip)
1870
Tao Bao85f16982018-03-08 16:28:33 -08001871 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1872 # all the info of the latter. However, system updaters and OTA servers need to
1873 # take time to switch to the new flag. We keep both of the flags for
1874 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001875 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001876 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001877 StreamingPropertyFiles(),
1878 )
1879 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001880
Tao Baoc098e9e2016-01-07 13:03:56 -08001881
Doug Zongkereef39442009-04-02 12:14:19 -07001882def main(argv):
1883
1884 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001885 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001886 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001887 elif o in ("-i", "--incremental_from"):
1888 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001889 elif o == "--full_radio":
1890 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001891 elif o == "--full_bootloader":
1892 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001893 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001894 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001895 elif o == "--downgrade":
1896 OPTIONS.downgrade = True
1897 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001898 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001899 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001900 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001901 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001902 elif o == "--oem_no_mount":
1903 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001904 elif o in ("-e", "--extra_script"):
1905 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001906 elif o in ("-t", "--worker_threads"):
1907 if a.isdigit():
1908 OPTIONS.worker_threads = int(a)
1909 else:
1910 raise ValueError("Cannot parse value %r for option %r - only "
1911 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001912 elif o in ("-2", "--two_step"):
1913 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08001914 elif o == "--include_secondary":
1915 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08001916 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001917 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001918 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001919 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001920 elif o == "--block":
1921 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001922 elif o in ("-b", "--binary"):
1923 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07001924 elif o == "--stash_threshold":
1925 try:
1926 OPTIONS.stash_threshold = float(a)
1927 except ValueError:
1928 raise ValueError("Cannot parse value %r for option %r - expecting "
1929 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08001930 elif o == "--log_diff":
1931 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001932 elif o == "--payload_signer":
1933 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001934 elif o == "--payload_signer_args":
1935 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001936 elif o == "--extracted_input_target_files":
1937 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08001938 elif o == "--skip_postinstall":
1939 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08001940 elif o == "--retrofit_dynamic_partitions":
1941 OPTIONS.retrofit_dynamic_partitions = True
Doug Zongkereef39442009-04-02 12:14:19 -07001942 else:
1943 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001944 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001945
1946 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08001947 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001948 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07001949 "package_key=",
1950 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001951 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001952 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001953 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08001954 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08001955 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001956 "extra_script=",
1957 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001958 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08001959 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07001960 "no_signing",
1961 "block",
1962 "binary=",
1963 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001964 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001965 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07001966 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001967 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001968 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001969 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07001970 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08001971 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08001972 "retrofit_dynamic_partitions",
Dan Albert8b72aef2015-03-23 19:13:21 -07001973 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001974
1975 if len(args) != 2:
1976 common.Usage(__doc__)
1977 sys.exit(1)
1978
Tao Bao32fcdab2018-10-12 10:30:39 -07001979 common.InitLogging()
1980
Tao Bao5d182562016-02-23 11:38:39 -08001981 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08001982 # We should only allow downgrading incrementals (as opposed to full).
1983 # Otherwise the device may go back from arbitrary build with this full
1984 # OTA package.
1985 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07001986 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08001987
Tao Bao2db13852018-01-08 22:28:57 -08001988 # Load the build info dicts from the zip directly or the extracted input
1989 # directory. We don't need to unzip the entire target-files zips, because they
1990 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1991 # When loading the info dicts, we don't need to provide the second parameter
1992 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1993 # some properties with their actual paths, such as 'selinux_fc',
1994 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07001995 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08001996 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07001997 else:
Tao Bao2db13852018-01-08 22:28:57 -08001998 with zipfile.ZipFile(args[0], 'r') as input_zip:
1999 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002000
Tao Bao32fcdab2018-10-12 10:30:39 -07002001 logger.info("--- target info ---")
2002 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002003
2004 # Load the source build dict if applicable.
2005 if OPTIONS.incremental_source is not None:
2006 OPTIONS.target_info_dict = OPTIONS.info_dict
2007 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2008 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2009
Tao Bao32fcdab2018-10-12 10:30:39 -07002010 logger.info("--- source info ---")
2011 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002012
2013 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002014 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2015
Yifan Hong50e79542018-11-08 17:44:12 -08002016 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002017 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002018 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002019 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2020 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002021 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2022 raise common.ExternalError(
2023 "Expect to generate incremental OTA for retrofitting dynamic "
2024 "partitions, but dynamic_partition_retrofit is not set in target "
2025 "build.")
2026 logger.info("Implicitly generating retrofit incremental OTA.")
2027 OPTIONS.retrofit_dynamic_partitions = True
2028
2029 # Skip postinstall for retrofitting dynamic partitions.
2030 if OPTIONS.retrofit_dynamic_partitions:
2031 OPTIONS.skip_postinstall = True
2032
Tao Baoc098e9e2016-01-07 13:03:56 -08002033 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2034
Christian Oderf63e2cd2017-05-01 22:30:15 +02002035 # Use the default key to sign the package if not specified with package_key.
2036 # package_keys are needed on ab_updates, so always define them if an
2037 # ab_update is getting created.
2038 if not OPTIONS.no_signing or ab_update:
2039 if OPTIONS.package_key is None:
2040 OPTIONS.package_key = OPTIONS.info_dict.get(
2041 "default_system_dev_certificate",
2042 "build/target/product/security/testkey")
2043 # Get signing keys
2044 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2045
Tao Baoc098e9e2016-01-07 13:03:56 -08002046 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002047 WriteABOTAPackageWithBrilloScript(
2048 target_file=args[0],
2049 output_file=args[1],
2050 source_file=OPTIONS.incremental_source)
2051
Tao Bao32fcdab2018-10-12 10:30:39 -07002052 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002053 return
2054
Tao Bao2db13852018-01-08 22:28:57 -08002055 # Sanity check the loaded info dicts first.
2056 if OPTIONS.info_dict.get("no_recovery") == "true":
2057 raise common.ExternalError(
2058 "--- target build has specified no recovery ---")
2059
2060 # Non-A/B OTAs rely on /cache partition to store temporary files.
2061 cache_size = OPTIONS.info_dict.get("cache_size")
2062 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002063 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002064 OPTIONS.cache_size = cache_size
2065
Doug Zongker1c390a22009-05-14 19:06:36 -07002066 if OPTIONS.extra_script is not None:
2067 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2068
Dan Willemsencea5cd22017-03-21 14:44:27 -07002069 if OPTIONS.extracted_input is not None:
2070 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002071 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002072 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002073 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002074 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002075
Tao Bao2db13852018-01-08 22:28:57 -08002076 # If the caller explicitly specified the device-specific extensions path via
2077 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2078 # is present in the target target_files. Otherwise, take the path of the file
2079 # from 'tool_extensions' in the info dict and look for that in the local
2080 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002081 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002082 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2083 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002084 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002085 OPTIONS.device_specific = from_input
2086 else:
Tao Bao2db13852018-01-08 22:28:57 -08002087 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002088
Doug Zongker37974732010-09-16 17:44:38 -07002089 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002090 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002091
Tao Bao767e3ac2015-11-10 12:19:19 -08002092 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002093 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002094 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002095 WriteFullOTAPackage(
2096 input_zip,
2097 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002098
Tao Bao32b80dc2018-01-08 22:50:47 -08002099 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002100 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002101 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002102 OPTIONS.source_tmp = common.UnzipTemp(
2103 OPTIONS.incremental_source, UNZIP_PATTERN)
2104 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2105 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002106 WriteBlockIncrementalOTAPackage(
2107 input_zip,
2108 source_zip,
2109 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002110
2111 if OPTIONS.log_diff:
2112 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002113 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002114 target_files_diff.recursiveDiff(
2115 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002116
Tao Bao32fcdab2018-10-12 10:30:39 -07002117 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002118
2119
2120if __name__ == '__main__':
2121 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002122 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002123 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002124 except common.ExternalError:
2125 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002126 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002127 finally:
2128 common.Cleanup()