blob: 4336cb31fa484f558bb52dc0d718b82c4d31ca08 [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
Steven Laver9e73e822019-01-29 20:20:08 -0800262 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
263 "ro.product.manufacturer", "ro.product.model",
264 "ro.product.name"]
265 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "product_services",
266 "odm", "vendor", "system"]
267
Tao Bao481bab82017-12-21 11:23:09 -0800268 def __init__(self, info_dict, oem_dicts):
269 """Initializes a BuildInfo instance with the given dicts.
270
Tao Bao667c7532018-07-06 10:13:59 -0700271 Note that it only wraps up the given dicts, without making copies.
272
Tao Bao481bab82017-12-21 11:23:09 -0800273 Arguments:
274 info_dict: The build-time info dict.
275 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
276 that it always uses the first dict to calculate the fingerprint or the
277 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700278 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800279 """
280 self.info_dict = info_dict
281 self.oem_dicts = oem_dicts
282
283 self._is_ab = info_dict.get("ab_update") == "true"
284 self._oem_props = info_dict.get("oem_fingerprint_properties")
285
286 if self._oem_props:
287 assert oem_dicts, "OEM source required for this build"
288
289 # These two should be computed only after setting self._oem_props.
290 self._device = self.GetOemProperty("ro.product.device")
291 self._fingerprint = self.CalculateFingerprint()
292
293 @property
294 def is_ab(self):
295 return self._is_ab
296
297 @property
298 def device(self):
299 return self._device
300
301 @property
302 def fingerprint(self):
303 return self._fingerprint
304
305 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700306 def vendor_fingerprint(self):
307 if "vendor.build.prop" not in self.info_dict:
308 return None
309 vendor_build_prop = self.info_dict["vendor.build.prop"]
310 if "ro.vendor.build.fingerprint" in vendor_build_prop:
311 return vendor_build_prop["ro.vendor.build.fingerprint"]
312 if "ro.vendor.build.thumbprint" in vendor_build_prop:
313 return vendor_build_prop["ro.vendor.build.thumbprint"]
314 return None
315
316 @property
Tao Bao481bab82017-12-21 11:23:09 -0800317 def oem_props(self):
318 return self._oem_props
319
320 def __getitem__(self, key):
321 return self.info_dict[key]
322
Tao Bao667c7532018-07-06 10:13:59 -0700323 def __setitem__(self, key, value):
324 self.info_dict[key] = value
325
Tao Bao481bab82017-12-21 11:23:09 -0800326 def get(self, key, default=None):
327 return self.info_dict.get(key, default)
328
Tao Bao667c7532018-07-06 10:13:59 -0700329 def items(self):
330 return self.info_dict.items()
331
Tao Bao481bab82017-12-21 11:23:09 -0800332 def GetBuildProp(self, prop):
333 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800334 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
335 return self._ResolveRoProductBuildProp(prop)
336
Tao Bao481bab82017-12-21 11:23:09 -0800337 try:
338 return self.info_dict.get("build.prop", {})[prop]
339 except KeyError:
340 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
341
Steven Laver9e73e822019-01-29 20:20:08 -0800342 def _ResolveRoProductBuildProp(self, prop):
343 """Resolves the inquired ro.product.* build property"""
344 prop_val = self.info_dict.get("build.prop", {}).get(prop)
345 if prop_val:
346 return prop_val
347
348 source_order_val = self.info_dict.get("build.prop", {}).get(
349 "ro.product.property_source_order")
350 if source_order_val:
351 source_order = source_order_val.split(",")
352 else:
353 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
354
355 # Check that all sources in ro.product.property_source_order are valid
356 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
357 for x in source_order]):
358 raise common.ExternalError(
359 "Invalid ro.product.property_source_order '{}'".format(source_order))
360
361 for source in source_order:
362 source_prop = prop.replace("ro.product", "ro.product.{}".format(source),
363 1)
364 prop_val = self.info_dict.get("{}.build.prop".format(source), {}).get(
365 source_prop)
366 if prop_val:
367 return prop_val
368
369 raise common.ExternalError("couldn't resolve {}".format(prop))
370
Tao Bao481bab82017-12-21 11:23:09 -0800371 def GetVendorBuildProp(self, prop):
372 """Returns the inquired vendor build property."""
373 try:
374 return self.info_dict.get("vendor.build.prop", {})[prop]
375 except KeyError:
376 raise common.ExternalError(
377 "couldn't find %s in vendor.build.prop" % (prop,))
378
379 def GetOemProperty(self, key):
380 if self.oem_props is not None and key in self.oem_props:
381 return self.oem_dicts[0][key]
382 return self.GetBuildProp(key)
383
384 def CalculateFingerprint(self):
385 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800386 try:
387 return self.GetBuildProp("ro.build.fingerprint")
388 except common.ExternalError:
389 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
390 self.GetBuildProp("ro.product.brand"),
391 self.GetBuildProp("ro.product.name"),
392 self.GetBuildProp("ro.product.device"),
393 self.GetBuildProp("ro.build.version.release"),
394 self.GetBuildProp("ro.build.id"),
395 self.GetBuildProp("ro.build.version.incremental"),
396 self.GetBuildProp("ro.build.type"),
397 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800398 return "%s/%s/%s:%s" % (
399 self.GetOemProperty("ro.product.brand"),
400 self.GetOemProperty("ro.product.name"),
401 self.GetOemProperty("ro.product.device"),
402 self.GetBuildProp("ro.build.thumbprint"))
403
404 def WriteMountOemScript(self, script):
405 assert self.oem_props is not None
406 recovery_mount_options = self.info_dict.get("recovery_mount_options")
407 script.Mount("/oem", recovery_mount_options)
408
409 def WriteDeviceAssertions(self, script, oem_no_mount):
410 # Read the property directly if not using OEM properties.
411 if not self.oem_props:
412 script.AssertDevice(self.device)
413 return
414
415 # Otherwise assert OEM properties.
416 if not self.oem_dicts:
417 raise common.ExternalError(
418 "No OEM file provided to answer expected assertions")
419
420 for prop in self.oem_props.split():
421 values = []
422 for oem_dict in self.oem_dicts:
423 if prop in oem_dict:
424 values.append(oem_dict[prop])
425 if not values:
426 raise common.ExternalError(
427 "The OEM file is missing the property %s" % (prop,))
428 script.AssertOemProperty(prop, values, oem_no_mount)
429
430
Tao Baofabe0832018-01-17 15:52:28 -0800431class PayloadSigner(object):
432 """A class that wraps the payload signing works.
433
434 When generating a Payload, hashes of the payload and metadata files will be
435 signed with the device key, either by calling an external payload signer or
436 by calling openssl with the package key. This class provides a unified
437 interface, so that callers can just call PayloadSigner.Sign().
438
439 If an external payload signer has been specified (OPTIONS.payload_signer), it
440 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
441 that the signing key should be provided as part of the payload_signer_args.
442 Otherwise without an external signer, it uses the package key
443 (OPTIONS.package_key) and calls openssl for the signing works.
444 """
445
446 def __init__(self):
447 if OPTIONS.payload_signer is None:
448 # Prepare the payload signing key.
449 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
450 pw = OPTIONS.key_passwords[OPTIONS.package_key]
451
452 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
453 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
454 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
455 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700456 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800457
458 self.signer = "openssl"
459 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
460 "-pkeyopt", "digest:sha256"]
461 else:
462 self.signer = OPTIONS.payload_signer
463 self.signer_args = OPTIONS.payload_signer_args
464
465 def Sign(self, in_file):
466 """Signs the given input file. Returns the output filename."""
467 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
468 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700469 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800470 return out_file
471
472
Tao Bao40b18822018-01-30 18:19:04 -0800473class Payload(object):
474 """Manages the creation and the signing of an A/B OTA Payload."""
475
476 PAYLOAD_BIN = 'payload.bin'
477 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800478 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
479 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800480
Tao Bao667ff572018-02-10 00:02:40 -0800481 def __init__(self, secondary=False):
482 """Initializes a Payload instance.
483
484 Args:
485 secondary: Whether it's generating a secondary payload (default: False).
486 """
Tao Bao40b18822018-01-30 18:19:04 -0800487 self.payload_file = None
488 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800489 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800490
491 def Generate(self, target_file, source_file=None, additional_args=None):
492 """Generates a payload from the given target-files zip(s).
493
494 Args:
495 target_file: The filename of the target build target-files zip.
496 source_file: The filename of the source build target-files zip; or None if
497 generating a full OTA.
498 additional_args: A list of additional args that should be passed to
499 brillo_update_payload script; or None.
500 """
501 if additional_args is None:
502 additional_args = []
503
504 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
505 cmd = ["brillo_update_payload", "generate",
506 "--payload", payload_file,
507 "--target_image", target_file]
508 if source_file is not None:
509 cmd.extend(["--source_image", source_file])
510 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700511 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800512
513 self.payload_file = payload_file
514 self.payload_properties = None
515
516 def Sign(self, payload_signer):
517 """Generates and signs the hashes of the payload and metadata.
518
519 Args:
520 payload_signer: A PayloadSigner() instance that serves the signing work.
521
522 Raises:
523 AssertionError: On any failure when calling brillo_update_payload script.
524 """
525 assert isinstance(payload_signer, PayloadSigner)
526
527 # 1. Generate hashes of the payload and metadata files.
528 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
529 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
530 cmd = ["brillo_update_payload", "hash",
531 "--unsigned_payload", self.payload_file,
532 "--signature_size", "256",
533 "--metadata_hash_file", metadata_sig_file,
534 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700535 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800536
537 # 2. Sign the hashes.
538 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
539 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
540
541 # 3. Insert the signatures back into the payload file.
542 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
543 suffix=".bin")
544 cmd = ["brillo_update_payload", "sign",
545 "--unsigned_payload", self.payload_file,
546 "--payload", signed_payload_file,
547 "--signature_size", "256",
548 "--metadata_signature_file", signed_metadata_sig_file,
549 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700550 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800551
552 # 4. Dump the signed payload properties.
553 properties_file = common.MakeTempFile(prefix="payload-properties-",
554 suffix=".txt")
555 cmd = ["brillo_update_payload", "properties",
556 "--payload", signed_payload_file,
557 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700558 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800559
Tao Bao667ff572018-02-10 00:02:40 -0800560 if self.secondary:
561 with open(properties_file, "a") as f:
562 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
563
Tao Bao40b18822018-01-30 18:19:04 -0800564 if OPTIONS.wipe_user_data:
565 with open(properties_file, "a") as f:
566 f.write("POWERWASH=1\n")
567
568 self.payload_file = signed_payload_file
569 self.payload_properties = properties_file
570
Tao Bao667ff572018-02-10 00:02:40 -0800571 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800572 """Writes the payload to the given zip.
573
574 Args:
575 output_zip: The output ZipFile instance.
576 """
577 assert self.payload_file is not None
578 assert self.payload_properties is not None
579
Tao Bao667ff572018-02-10 00:02:40 -0800580 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800581 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
582 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
583 else:
584 payload_arcname = Payload.PAYLOAD_BIN
585 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
586
Tao Bao40b18822018-01-30 18:19:04 -0800587 # Add the signed payload file and properties into the zip. In order to
588 # support streaming, we pack them as ZIP_STORED. So these entries can be
589 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800590 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800591 compress_type=zipfile.ZIP_STORED)
592 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800593 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800594 compress_type=zipfile.ZIP_STORED)
595
596
Doug Zongkereef39442009-04-02 12:14:19 -0700597def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200598 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700599
Doug Zongker951495f2009-08-14 12:44:19 -0700600 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
601 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700602
603
Tao Bao481bab82017-12-21 11:23:09 -0800604def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800605 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800606 if not oem_source:
607 return None
608
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800609 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800610 for oem_file in oem_source:
611 with open(oem_file) as fp:
612 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800613 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700614
Doug Zongkereef39442009-04-02 12:14:19 -0700615
Tao Baod42e97e2016-11-30 12:11:57 -0800616def _WriteRecoveryImageToBoot(script, output_zip):
617 """Find and write recovery image to /boot in two-step OTA.
618
619 In two-step OTAs, we write recovery image to /boot as the first step so that
620 we can reboot to there and install a new recovery image to /recovery.
621 A special "recovery-two-step.img" will be preferred, which encodes the correct
622 path of "/boot". Otherwise the device may show "device is corrupt" message
623 when booting into /boot.
624
625 Fall back to using the regular recovery.img if the two-step recovery image
626 doesn't exist. Note that rebuilding the special image at this point may be
627 infeasible, because we don't have the desired boot signer and keys when
628 calling ota_from_target_files.py.
629 """
630
631 recovery_two_step_img_name = "recovery-two-step.img"
632 recovery_two_step_img_path = os.path.join(
633 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
634 if os.path.exists(recovery_two_step_img_path):
635 recovery_two_step_img = common.GetBootableImage(
636 recovery_two_step_img_name, recovery_two_step_img_name,
637 OPTIONS.input_tmp, "RECOVERY")
638 common.ZipWriteStr(
639 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700640 logger.info(
641 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800642 script.WriteRawImage("/boot", recovery_two_step_img_name)
643 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700644 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800645 # The "recovery.img" entry has been written into package earlier.
646 script.WriteRawImage("/boot", "recovery.img")
647
648
Doug Zongkerc9253822014-02-04 12:17:58 -0800649def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700650 namelist = [name for name in target_files_zip.namelist()]
651 return ("SYSTEM/recovery-from-boot.p" in namelist or
652 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700653
Tao Bao457cbf62017-03-06 09:56:01 -0800654
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700655def HasVendorPartition(target_files_zip):
656 try:
657 target_files_zip.getinfo("VENDOR/")
658 return True
659 except KeyError:
660 return False
661
Tao Bao457cbf62017-03-06 09:56:01 -0800662
Tao Bao481bab82017-12-21 11:23:09 -0800663def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700664 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800665 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700666
667
Tao Bao481bab82017-12-21 11:23:09 -0800668def WriteFingerprintAssertion(script, target_info, source_info):
669 source_oem_props = source_info.oem_props
670 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700671
Tao Bao481bab82017-12-21 11:23:09 -0800672 if source_oem_props is None and target_oem_props is None:
673 script.AssertSomeFingerprint(
674 source_info.fingerprint, target_info.fingerprint)
675 elif source_oem_props is not None and target_oem_props is not None:
676 script.AssertSomeThumbprint(
677 target_info.GetBuildProp("ro.build.thumbprint"),
678 source_info.GetBuildProp("ro.build.thumbprint"))
679 elif source_oem_props is None and target_oem_props is not None:
680 script.AssertFingerprintOrThumbprint(
681 source_info.fingerprint,
682 target_info.GetBuildProp("ro.build.thumbprint"))
683 else:
684 script.AssertFingerprintOrThumbprint(
685 target_info.fingerprint,
686 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700687
Doug Zongkerfc44a512014-08-26 13:10:25 -0700688
Tao Bao481bab82017-12-21 11:23:09 -0800689def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
690 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700691 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700692
693 Metadata used for on-device compatibility verification is retrieved from
694 target_zip then added to compatibility.zip which is added to the output_zip
695 archive.
696
Tao Baobcd1d162017-08-26 13:10:26 -0700697 Compatibility archive should only be included for devices that have enabled
698 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700699
700 Args:
701 target_zip: Zip file containing the source files to be included for OTA.
702 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800703 target_info: The BuildInfo instance that holds the target build info.
704 source_info: The BuildInfo instance that holds the source build info, if
705 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700706 """
707
Tao Baobcd1d162017-08-26 13:10:26 -0700708 def AddCompatibilityArchive(system_updated, vendor_updated):
709 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700710
Tao Baobcd1d162017-08-26 13:10:26 -0700711 Args:
712 system_updated: If True, the system image will be updated and therefore
713 its metadata should be included.
714 vendor_updated: If True, the vendor image will be updated and therefore
715 its metadata should be included.
716 """
717 # Determine what metadata we need. Files are names relative to META/.
718 compatibility_files = []
719 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
720 system_metadata = ("system_manifest.xml", "system_matrix.xml")
721 if vendor_updated:
722 compatibility_files += vendor_metadata
723 if system_updated:
724 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700725
Tao Baobcd1d162017-08-26 13:10:26 -0700726 # Create new archive.
727 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800728 compatibility_archive_zip = zipfile.ZipFile(
729 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700730
Tao Baobcd1d162017-08-26 13:10:26 -0700731 # Add metadata.
732 for file_name in compatibility_files:
733 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700734
Tao Baobcd1d162017-08-26 13:10:26 -0700735 if target_file_name in target_zip.namelist():
736 data = target_zip.read(target_file_name)
737 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700738
Tao Baobcd1d162017-08-26 13:10:26 -0700739 # Ensure files are written before we copy into output_zip.
740 compatibility_archive_zip.close()
741
742 # Only add the archive if we have any compatibility info.
743 if compatibility_archive_zip.namelist():
744 common.ZipWrite(output_zip, compatibility_archive.name,
745 arcname="compatibility.zip",
746 compress_type=zipfile.ZIP_STORED)
747
748 # Will only proceed if the target has enabled the Treble support (as well as
749 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800750 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700751 return
752
Tao Baobcd1d162017-08-26 13:10:26 -0700753 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800754 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700755 AddCompatibilityArchive(True, True)
756 return
757
Tao Bao481bab82017-12-21 11:23:09 -0800758 source_fp = source_info.fingerprint
759 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700760 system_updated = source_fp != target_fp
761
Tao Baoea6cbd02018-09-05 13:06:37 -0700762 source_fp_vendor = source_info.vendor_fingerprint
763 target_fp_vendor = target_info.vendor_fingerprint
764 # vendor build fingerprints could be possibly blacklisted at build time. For
765 # such a case, we consider the vendor images being changed.
766 if source_fp_vendor is None or target_fp_vendor is None:
767 vendor_updated = True
768 else:
769 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700770
771 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700772
773
Tao Bao491d7e22018-02-21 13:17:22 -0800774def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800775 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700776
Tao Bao481bab82017-12-21 11:23:09 -0800777 # We don't know what version it will be installed on top of. We expect the API
778 # just won't change very often. Similarly for fstab, it might have changed in
779 # the target build.
780 target_api_version = target_info["recovery_api_version"]
781 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700782
Tao Bao481bab82017-12-21 11:23:09 -0800783 if target_info.oem_props and not OPTIONS.oem_no_mount:
784 target_info.WriteMountOemScript(script)
785
Tao Baodf3a48b2018-01-10 16:30:43 -0800786 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700787
Tao Bao491d7e22018-02-21 13:17:22 -0800788 if not OPTIONS.no_signing:
789 staging_file = common.MakeTempFile(suffix='.zip')
790 else:
791 staging_file = output_file
792
793 output_zip = zipfile.ZipFile(
794 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
795
Doug Zongker05d3dea2009-06-22 11:32:31 -0700796 device_specific = common.DeviceSpecificParams(
797 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800798 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700799 output_zip=output_zip,
800 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700801 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700802 metadata=metadata,
803 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700804
Tao Bao457cbf62017-03-06 09:56:01 -0800805 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800806
Tao Bao481bab82017-12-21 11:23:09 -0800807 # Assertions (e.g. downgrade check, device properties check).
808 ts = target_info.GetBuildProp("ro.build.date.utc")
809 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700810 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700811
Tao Bao481bab82017-12-21 11:23:09 -0800812 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700813 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800814
815 # Two-step package strategy (in chronological order, which is *not*
816 # the order in which the generated script has things):
817 #
818 # if stage is not "2/3" or "3/3":
819 # write recovery image to boot partition
820 # set stage to "2/3"
821 # reboot to boot partition and restart recovery
822 # else if stage is "2/3":
823 # write recovery image to recovery partition
824 # set stage to "3/3"
825 # reboot to recovery partition and restart recovery
826 # else:
827 # (stage must be "3/3")
828 # set stage to ""
829 # do normal full package installation:
830 # wipe and install system, boot image, etc.
831 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700832 # complete script normally
833 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800834
835 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
836 OPTIONS.input_tmp, "RECOVERY")
837 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800838 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800839 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800840 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800841 assert fs.fs_type.upper() == "EMMC", \
842 "two-step packages only supported on devices with EMMC /misc partitions"
843 bcb_dev = {"bcb_dev": fs.device}
844 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
845 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700846if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800847""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800848
849 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
850 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800851 script.WriteRawImage("/recovery", "recovery.img")
852 script.AppendExtra("""
853set_stage("%(bcb_dev)s", "3/3");
854reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700855else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800856""" % bcb_dev)
857
Tao Baod42e97e2016-11-30 12:11:57 -0800858 # Stage 3/3: Make changes.
859 script.Comment("Stage 3/3")
860
Tao Bao6c55a8a2015-04-08 15:30:27 -0700861 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800862 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700863
Doug Zongkere5ff5902012-01-17 10:55:37 -0800864 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700865
Doug Zongker01ce19c2014-02-04 13:48:15 -0800866 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700867
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700868 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800869 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700870 if HasVendorPartition(input_zip):
871 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700872
Doug Zongker4b9596f2014-06-09 14:15:45 -0700873 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800874
Tao Baoe709b092018-02-07 12:40:00 -0800875 # See the notes in WriteBlockIncrementalOTAPackage().
876 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
877
Yifan Hong10c530d2018-12-27 17:34:18 -0800878 def GetBlockDifference(partition):
879 # Full OTA is done as an "incremental" against an empty source image. This
880 # has the effect of writing new data from the package to the entire
881 # partition, but lets us reuse the updater code that writes incrementals to
882 # do it.
883 tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
884 allow_shared_blocks)
885 tgt.ResetFileMap()
886 diff = common.BlockDifference(partition, tgt, src=None)
887 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700888
Yifan Hong10c530d2018-12-27 17:34:18 -0800889 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
890 if device_specific_diffs:
891 assert all(isinstance(diff, common.BlockDifference)
892 for diff in device_specific_diffs), \
893 "FullOTA_GetBlockDifferences is not returning a list of " \
894 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800895
Yifan Hong10c530d2018-12-27 17:34:18 -0800896 progress_dict = dict()
897 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700898 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800899 block_diffs.append(GetBlockDifference("vendor"))
900 progress_dict["vendor"] = 0.1
901 if device_specific_diffs:
902 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700903
Yifan Hong10c530d2018-12-27 17:34:18 -0800904 if target_info.get('use_dynamic_partitions') == "true":
905 # Use empty source_info_dict to indicate that all partitions / groups must
906 # be re-added.
907 dynamic_partitions_diff = common.DynamicPartitionsDifference(
908 info_dict=OPTIONS.info_dict,
909 block_diffs=block_diffs,
910 progress_dict=progress_dict)
911 dynamic_partitions_diff.WriteScript(script, output_zip,
912 write_verify_script=OPTIONS.verify)
913 else:
914 for block_diff in block_diffs:
915 block_diff.WriteScript(script, output_zip,
916 progress=progress_dict.get(block_diff.partition),
917 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700918
Tao Bao481bab82017-12-21 11:23:09 -0800919 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700920
Yifan Hong10c530d2018-12-27 17:34:18 -0800921 boot_img = common.GetBootableImage(
922 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800923 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700924 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700925
Doug Zongker01ce19c2014-02-04 13:48:15 -0800926 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700927 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700928
Doug Zongker01ce19c2014-02-04 13:48:15 -0800929 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700930 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700931
Doug Zongker1c390a22009-05-14 19:06:36 -0700932 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700933 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700934
Doug Zongker14833602010-02-02 13:12:04 -0800935 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800936
Doug Zongker922206e2014-03-04 13:16:24 -0800937 if OPTIONS.wipe_user_data:
938 script.ShowProgress(0.1, 10)
939 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700940
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800941 if OPTIONS.two_step:
942 script.AppendExtra("""
943set_stage("%(bcb_dev)s", "");
944""" % bcb_dev)
945 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800946
947 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
948 script.Comment("Stage 1/3")
949 _WriteRecoveryImageToBoot(script, output_zip)
950
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800951 script.AppendExtra("""
952set_stage("%(bcb_dev)s", "2/3");
953reboot_now("%(bcb_dev)s", "");
954endif;
955endif;
956""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800957
Tao Bao5d182562016-02-23 11:38:39 -0800958 script.SetProgress(1)
959 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800960 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800961
962 # We haven't written the metadata entry, which will be done in
963 # FinalizeMetadata.
964 common.ZipClose(output_zip)
965
966 needed_property_files = (
967 NonAbOtaPropertyFiles(),
968 )
969 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700970
Doug Zongkerfc44a512014-08-26 13:10:25 -0700971
Doug Zongker2ea21062010-04-28 16:05:21 -0700972def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800973 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
974 common.ZipWriteStr(output_zip, METADATA_NAME, value,
975 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700976
Doug Zongkerfc44a512014-08-26 13:10:25 -0700977
Tao Bao481bab82017-12-21 11:23:09 -0800978def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800979 # Only incremental OTAs are allowed to reach here.
980 assert OPTIONS.incremental_source is not None
981
Tao Bao481bab82017-12-21 11:23:09 -0800982 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
983 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800984 is_downgrade = long(post_timestamp) < long(pre_timestamp)
985
986 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800987 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700988 raise RuntimeError(
989 "--downgrade or --override_timestamp specified but no downgrade "
990 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800991 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800992 else:
993 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700994 raise RuntimeError(
995 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
996 "Need to specify --override_timestamp OR --downgrade to allow "
997 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800998
999
Tao Baodf3a48b2018-01-10 16:30:43 -08001000def GetPackageMetadata(target_info, source_info=None):
1001 """Generates and returns the metadata dict.
1002
1003 It generates a dict() that contains the info to be written into an OTA
1004 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001005 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001006
1007 Args:
1008 target_info: The BuildInfo instance that holds the target build info.
1009 source_info: The BuildInfo instance that holds the source build info, or
1010 None if generating full OTA.
1011
1012 Returns:
1013 A dict to be written into package metadata entry.
1014 """
1015 assert isinstance(target_info, BuildInfo)
1016 assert source_info is None or isinstance(source_info, BuildInfo)
1017
1018 metadata = {
1019 'post-build' : target_info.fingerprint,
1020 'post-build-incremental' : target_info.GetBuildProp(
1021 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001022 'post-sdk-level' : target_info.GetBuildProp(
1023 'ro.build.version.sdk'),
1024 'post-security-patch-level' : target_info.GetBuildProp(
1025 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001026 }
1027
1028 if target_info.is_ab:
1029 metadata['ota-type'] = 'AB'
1030 metadata['ota-required-cache'] = '0'
1031 else:
1032 metadata['ota-type'] = 'BLOCK'
1033
1034 if OPTIONS.wipe_user_data:
1035 metadata['ota-wipe'] = 'yes'
1036
1037 is_incremental = source_info is not None
1038 if is_incremental:
1039 metadata['pre-build'] = source_info.fingerprint
1040 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1041 'ro.build.version.incremental')
1042 metadata['pre-device'] = source_info.device
1043 else:
1044 metadata['pre-device'] = target_info.device
1045
Tao Baofaa8e0b2018-04-12 14:31:43 -07001046 # Use the actual post-timestamp, even for a downgrade case.
1047 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1048
1049 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001050 if is_incremental:
1051 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001052
1053 return metadata
1054
1055
Tao Baod3fc38a2018-03-08 16:09:01 -08001056class PropertyFiles(object):
1057 """A class that computes the property-files string for an OTA package.
1058
1059 A property-files string is a comma-separated string that contains the
1060 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1061 can be fetched directly with the package URL along with the offset/size info.
1062 These strings can be used for streaming A/B OTAs, or allowing an updater to
1063 download package metadata entry directly, without paying the cost of
1064 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001065
Tao Baocc8e2662018-03-01 19:30:00 -08001066 Computing the final property-files string requires two passes. Because doing
1067 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1068 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1069 values.
1070
1071 This class provides functions to be called for each pass. The general flow is
1072 as follows.
1073
Tao Baod3fc38a2018-03-08 16:09:01 -08001074 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001075 # The first pass, which writes placeholders before doing initial signing.
1076 property_files.Compute()
1077 SignOutput()
1078
1079 # The second pass, by replacing the placeholders with actual data.
1080 property_files.Finalize()
1081 SignOutput()
1082
1083 And the caller can additionally verify the final result.
1084
1085 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001086 """
1087
Tao Baocc8e2662018-03-01 19:30:00 -08001088 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001089 self.name = None
1090 self.required = ()
1091 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001092
Tao Baocc8e2662018-03-01 19:30:00 -08001093 def Compute(self, input_zip):
1094 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001095
Tao Baocc8e2662018-03-01 19:30:00 -08001096 We reserve extra space for the offset and size of the metadata entry itself,
1097 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001098
Tao Baocc8e2662018-03-01 19:30:00 -08001099 Args:
1100 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001101
Tao Baocc8e2662018-03-01 19:30:00 -08001102 Returns:
1103 A string with placeholders for the metadata offset/size info, e.g.
1104 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1105 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001106 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001107
Tao Baod2ce2ed2018-03-16 12:59:42 -07001108 class InsufficientSpaceException(Exception):
1109 pass
1110
Tao Baocc8e2662018-03-01 19:30:00 -08001111 def Finalize(self, input_zip, reserved_length):
1112 """Finalizes a property-files string with actual METADATA offset/size info.
1113
1114 The input ZIP file has been signed, with the ZIP entries in the desired
1115 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1116 the ZIP entry offsets and construct the property-files string with actual
1117 data. Note that during this process, we must pad the property-files string
1118 to the reserved length, so that the METADATA entry size remains the same.
1119 Otherwise the entries' offsets and sizes may change again.
1120
1121 Args:
1122 input_zip: The input ZIP file.
1123 reserved_length: The reserved length of the property-files string during
1124 the call to Compute(). The final string must be no more than this
1125 size.
1126
1127 Returns:
1128 A property-files string including the metadata offset/size info, e.g.
1129 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1130
1131 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001132 InsufficientSpaceException: If the reserved length is insufficient to hold
1133 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001134 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001135 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001136 if len(result) > reserved_length:
1137 raise self.InsufficientSpaceException(
1138 'Insufficient reserved space: reserved={}, actual={}'.format(
1139 reserved_length, len(result)))
1140
Tao Baocc8e2662018-03-01 19:30:00 -08001141 result += ' ' * (reserved_length - len(result))
1142 return result
1143
1144 def Verify(self, input_zip, expected):
1145 """Verifies the input ZIP file contains the expected property-files string.
1146
1147 Args:
1148 input_zip: The input ZIP file.
1149 expected: The property-files string that's computed from Finalize().
1150
1151 Raises:
1152 AssertionError: On finding a mismatch.
1153 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001154 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001155 assert actual == expected, \
1156 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1157
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001158 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1159 """
1160 Constructs the property-files string per request.
1161
1162 Args:
1163 zip_file: The input ZIP file.
1164 reserved_length: The reserved length of the property-files string.
1165
1166 Returns:
1167 A property-files string including the metadata offset/size info, e.g.
1168 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1169 """
Tao Baocc8e2662018-03-01 19:30:00 -08001170
1171 def ComputeEntryOffsetSize(name):
1172 """Computes the zip entry offset and size."""
1173 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001174 offset = info.header_offset
1175 offset += zipfile.sizeFileHeader
1176 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001177 size = info.file_size
1178 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1179
1180 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001181 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001182 for entry in self.required:
1183 tokens.append(ComputeEntryOffsetSize(entry))
1184 for entry in self.optional:
1185 if entry in zip_file.namelist():
1186 tokens.append(ComputeEntryOffsetSize(entry))
1187
1188 # 'META-INF/com/android/metadata' is required. We don't know its actual
1189 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001190 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1191 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1192 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1193 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001194 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001195 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001196 else:
1197 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1198
1199 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001200
Tao Bao85f16982018-03-08 16:28:33 -08001201 def _GetPrecomputed(self, input_zip):
1202 """Computes the additional tokens to be included into the property-files.
1203
1204 This applies to tokens without actual ZIP entries, such as
1205 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1206 that they can download the payload metadata directly with the info.
1207
1208 Args:
1209 input_zip: The input zip file.
1210
1211 Returns:
1212 A list of strings (tokens) to be added to the property-files string.
1213 """
1214 # pylint: disable=no-self-use
1215 # pylint: disable=unused-argument
1216 return []
1217
Tao Baofe5b69a2018-03-02 09:47:43 -08001218
Tao Baod3fc38a2018-03-08 16:09:01 -08001219class StreamingPropertyFiles(PropertyFiles):
1220 """A subclass for computing the property-files for streaming A/B OTAs."""
1221
1222 def __init__(self):
1223 super(StreamingPropertyFiles, self).__init__()
1224 self.name = 'ota-streaming-property-files'
1225 self.required = (
1226 # payload.bin and payload_properties.txt must exist.
1227 'payload.bin',
1228 'payload_properties.txt',
1229 )
1230 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001231 # care_map is available only if dm-verity is enabled.
1232 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001233 'care_map.txt',
1234 # compatibility.zip is available only if target supports Treble.
1235 'compatibility.zip',
1236 )
1237
1238
Tao Bao85f16982018-03-08 16:28:33 -08001239class AbOtaPropertyFiles(StreamingPropertyFiles):
1240 """The property-files for A/B OTA that includes payload_metadata.bin info.
1241
1242 Since P, we expose one more token (aka property-file), in addition to the ones
1243 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1244 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1245 doesn't exist as a separate ZIP entry, but can be used to verify if the
1246 payload can be applied on the given device.
1247
1248 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1249 and the newly added 'ota-property-files' in P. The new token will only be
1250 available in 'ota-property-files'.
1251 """
1252
1253 def __init__(self):
1254 super(AbOtaPropertyFiles, self).__init__()
1255 self.name = 'ota-property-files'
1256
1257 def _GetPrecomputed(self, input_zip):
1258 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1259 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1260
1261 @staticmethod
1262 def _GetPayloadMetadataOffsetAndSize(input_zip):
1263 """Computes the offset and size of the payload metadata for a given package.
1264
1265 (From system/update_engine/update_metadata.proto)
1266 A delta update file contains all the deltas needed to update a system from
1267 one specific version to another specific version. The update format is
1268 represented by this struct pseudocode:
1269
1270 struct delta_update_file {
1271 char magic[4] = "CrAU";
1272 uint64 file_format_version;
1273 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1274
1275 // Only present if format_version > 1:
1276 uint32 metadata_signature_size;
1277
1278 // The Bzip2 compressed DeltaArchiveManifest
1279 char manifest[metadata_signature_size];
1280
1281 // The signature of the metadata (from the beginning of the payload up to
1282 // this location, not including the signature itself). This is a
1283 // serialized Signatures message.
1284 char medatada_signature_message[metadata_signature_size];
1285
1286 // Data blobs for files, no specific format. The specific offset
1287 // and length of each data blob is recorded in the DeltaArchiveManifest.
1288 struct {
1289 char data[];
1290 } blobs[];
1291
1292 // These two are not signed:
1293 uint64 payload_signatures_message_size;
1294 char payload_signatures_message[];
1295 };
1296
1297 'payload-metadata.bin' contains all the bytes from the beginning of the
1298 payload, till the end of 'medatada_signature_message'.
1299 """
1300 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001301 payload_offset = payload_info.header_offset
1302 payload_offset += zipfile.sizeFileHeader
1303 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001304 payload_size = payload_info.file_size
1305
1306 with input_zip.open('payload.bin', 'r') as payload_fp:
1307 header_bin = payload_fp.read(24)
1308
1309 # network byte order (big-endian)
1310 header = struct.unpack("!IQQL", header_bin)
1311
1312 # 'CrAU'
1313 magic = header[0]
1314 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1315
1316 manifest_size = header[2]
1317 metadata_signature_size = header[3]
1318 metadata_total = 24 + manifest_size + metadata_signature_size
1319 assert metadata_total < payload_size
1320
1321 return (payload_offset, metadata_total)
1322
1323
Tao Bao491d7e22018-02-21 13:17:22 -08001324class NonAbOtaPropertyFiles(PropertyFiles):
1325 """The property-files for non-A/B OTA.
1326
1327 For non-A/B OTA, the property-files string contains the info for METADATA
1328 entry, with which a system updater can be fetched the package metadata prior
1329 to downloading the entire package.
1330 """
1331
1332 def __init__(self):
1333 super(NonAbOtaPropertyFiles, self).__init__()
1334 self.name = 'ota-property-files'
1335
1336
Tao Baod3fc38a2018-03-08 16:09:01 -08001337def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001338 """Finalizes the metadata and signs an A/B OTA package.
1339
1340 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1341 that contains the offsets and sizes for the ZIP entries. An example
1342 property-files string is as follows.
1343
1344 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1345
1346 OTA server can pass down this string, in addition to the package URL, to the
1347 system update client. System update client can then fetch individual ZIP
1348 entries (ZIP_STORED) directly at the given offset of the URL.
1349
1350 Args:
1351 metadata: The metadata dict for the package.
1352 input_file: The input ZIP filename that doesn't contain the package METADATA
1353 entry yet.
1354 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001355 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001356 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001357
Tao Baod2ce2ed2018-03-16 12:59:42 -07001358 def ComputeAllPropertyFiles(input_file, needed_property_files):
1359 # Write the current metadata entry with placeholders.
1360 with zipfile.ZipFile(input_file) as input_zip:
1361 for property_files in needed_property_files:
1362 metadata[property_files.name] = property_files.Compute(input_zip)
1363 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001364
Tao Baod2ce2ed2018-03-16 12:59:42 -07001365 if METADATA_NAME in namelist:
1366 common.ZipDelete(input_file, METADATA_NAME)
1367 output_zip = zipfile.ZipFile(input_file, 'a')
1368 WriteMetadata(metadata, output_zip)
1369 common.ZipClose(output_zip)
1370
1371 if OPTIONS.no_signing:
1372 return input_file
1373
Tao Bao491d7e22018-02-21 13:17:22 -08001374 prelim_signing = common.MakeTempFile(suffix='.zip')
1375 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001376 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001377
Tao Baod2ce2ed2018-03-16 12:59:42 -07001378 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1379 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1380 for property_files in needed_property_files:
1381 metadata[property_files.name] = property_files.Finalize(
1382 prelim_signing_zip, len(metadata[property_files.name]))
1383
1384 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1385 # entries, as well as padding the entry headers. We do a preliminary signing
1386 # (with an incomplete metadata entry) to allow that to happen. Then compute
1387 # the ZIP entry offsets, write back the final metadata and do the final
1388 # signing.
1389 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1390 try:
1391 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1392 except PropertyFiles.InsufficientSpaceException:
1393 # Even with the preliminary signing, the entry orders may change
1394 # dramatically, which leads to insufficiently reserved space during the
1395 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1396 # preliminary signing works, based on the already ordered ZIP entries, to
1397 # address the issue.
1398 prelim_signing = ComputeAllPropertyFiles(
1399 prelim_signing, needed_property_files)
1400 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001401
1402 # Replace the METADATA entry.
1403 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001404 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001405 WriteMetadata(metadata, output_zip)
1406 common.ZipClose(output_zip)
1407
1408 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001409 if OPTIONS.no_signing:
1410 output_file = prelim_signing
1411 else:
1412 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001413
1414 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001415 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001416 for property_files in needed_property_files:
1417 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001418
1419
Tao Bao491d7e22018-02-21 13:17:22 -08001420def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001421 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1422 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001423
Tao Bao481bab82017-12-21 11:23:09 -08001424 target_api_version = target_info["recovery_api_version"]
1425 source_api_version = source_info["recovery_api_version"]
1426 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001427 logger.warning(
1428 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001429
Tao Bao481bab82017-12-21 11:23:09 -08001430 script = edify_generator.EdifyGenerator(
1431 source_api_version, target_info, fstab=source_info["fstab"])
1432
1433 if target_info.oem_props or source_info.oem_props:
1434 if not OPTIONS.oem_no_mount:
1435 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001436
Tao Baodf3a48b2018-01-10 16:30:43 -08001437 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001438
Tao Bao491d7e22018-02-21 13:17:22 -08001439 if not OPTIONS.no_signing:
1440 staging_file = common.MakeTempFile(suffix='.zip')
1441 else:
1442 staging_file = output_file
1443
1444 output_zip = zipfile.ZipFile(
1445 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1446
Geremy Condra36bd3652014-02-06 19:45:10 -08001447 device_specific = common.DeviceSpecificParams(
1448 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001449 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001450 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001451 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001452 output_zip=output_zip,
1453 script=script,
1454 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001455 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001456
Geremy Condra36bd3652014-02-06 19:45:10 -08001457 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001458 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001459 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001460 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001461 updating_boot = (not OPTIONS.two_step and
1462 (source_boot.data != target_boot.data))
1463
Geremy Condra36bd3652014-02-06 19:45:10 -08001464 target_recovery = common.GetBootableImage(
1465 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001466
Tao Baoe709b092018-02-07 12:40:00 -08001467 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1468 # shared blocks (i.e. some blocks will show up in multiple files' block
1469 # list). We can only allocate such shared blocks to the first "owner", and
1470 # disable imgdiff for all later occurrences.
1471 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1472 target_info.get('ext4_share_dup_blocks') == "true")
1473 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1474 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001475
1476 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1477 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001478 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001479 allow_shared_blocks,
1480 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001481
Tao Bao0582cb62017-12-21 11:47:01 -08001482 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001483 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001484 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001485
Tao Baof8acad12016-07-07 09:09:58 -07001486 # Check the first block of the source system partition for remount R/W only
1487 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001488 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001489 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001490 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1491 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1492 # b) the blocks listed in block map may not contain all the bytes for a given
1493 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001494 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001495 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1496 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001497 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001498 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001499 version=blockimgdiff_version,
1500 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001501
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001502 if HasVendorPartition(target_zip):
1503 if not HasVendorPartition(source_zip):
1504 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001505 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1506 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001507 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1508 "vendor", 4096, target_info)
1509 vendor_tgt = common.GetSparseImage(
1510 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1511 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001512
1513 # Check first block of vendor partition for remount R/W only if
1514 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001515 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001516 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001517 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001518 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001519 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001520 version=blockimgdiff_version,
1521 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001522 else:
1523 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001524
Tao Baobcd1d162017-08-26 13:10:26 -07001525 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001526 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001527
Tao Bao481bab82017-12-21 11:23:09 -08001528 # Assertions (e.g. device properties check).
1529 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001530 device_specific.IncrementalOTA_Assertions()
1531
1532 # Two-step incremental package strategy (in chronological order,
1533 # which is *not* the order in which the generated script has
1534 # things):
1535 #
1536 # if stage is not "2/3" or "3/3":
1537 # do verification on current system
1538 # write recovery image to boot partition
1539 # set stage to "2/3"
1540 # reboot to boot partition and restart recovery
1541 # else if stage is "2/3":
1542 # write recovery image to recovery partition
1543 # set stage to "3/3"
1544 # reboot to recovery partition and restart recovery
1545 # else:
1546 # (stage must be "3/3")
1547 # perform update:
1548 # patch system files, etc.
1549 # force full install of new boot image
1550 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001551 # complete script normally
1552 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001553
1554 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001555 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001556 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001557 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001558 assert fs.fs_type.upper() == "EMMC", \
1559 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001560 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001561 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1562 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001563if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001564""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001565
1566 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1567 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001568 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001569 script.WriteRawImage("/recovery", "recovery.img")
1570 script.AppendExtra("""
1571set_stage("%(bcb_dev)s", "3/3");
1572reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001573else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001574""" % bcb_dev)
1575
Tao Baod42e97e2016-11-30 12:11:57 -08001576 # Stage 1/3: (a) Verify the current system.
1577 script.Comment("Stage 1/3")
1578
Tao Bao6c55a8a2015-04-08 15:30:27 -07001579 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001580 script.Print("Source: {}".format(source_info.fingerprint))
1581 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001582
Geremy Condra36bd3652014-02-06 19:45:10 -08001583 script.Print("Verifying current system...")
1584
1585 device_specific.IncrementalOTA_VerifyBegin()
1586
Tao Bao481bab82017-12-21 11:23:09 -08001587 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001588
Tao Baod8d14be2016-02-04 14:26:02 -08001589 # Check the required cache size (i.e. stashed blocks).
1590 size = []
1591 if system_diff:
1592 size.append(system_diff.required_cache)
1593 if vendor_diff:
1594 size.append(vendor_diff.required_cache)
1595
Geremy Condra36bd3652014-02-06 19:45:10 -08001596 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001597 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001598 d = common.Difference(target_boot, source_boot)
1599 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001600 if d is None:
1601 include_full_boot = True
1602 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1603 else:
1604 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001605
Tao Bao32fcdab2018-10-12 10:30:39 -07001606 logger.info(
1607 "boot target: %d source: %d diff: %d", target_boot.size,
1608 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001609
Tao Bao51216552018-08-26 11:53:15 -07001610 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001611
Tao Bao51216552018-08-26 11:53:15 -07001612 script.PatchPartitionCheck(
1613 "{}:{}:{}:{}".format(
1614 boot_type, boot_device, target_boot.size, target_boot.sha1),
1615 "{}:{}:{}:{}".format(
1616 boot_type, boot_device, source_boot.size, source_boot.sha1))
1617
Tao Baod8d14be2016-02-04 14:26:02 -08001618 size.append(target_boot.size)
1619
1620 if size:
1621 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001622
1623 device_specific.IncrementalOTA_VerifyEnd()
1624
1625 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001626 # Stage 1/3: (b) Write recovery image to /boot.
1627 _WriteRecoveryImageToBoot(script, output_zip)
1628
Geremy Condra36bd3652014-02-06 19:45:10 -08001629 script.AppendExtra("""
1630set_stage("%(bcb_dev)s", "2/3");
1631reboot_now("%(bcb_dev)s", "");
1632else
1633""" % bcb_dev)
1634
Tao Baod42e97e2016-11-30 12:11:57 -08001635 # Stage 3/3: Make changes.
1636 script.Comment("Stage 3/3")
1637
Jesse Zhao75bcea02015-01-06 10:59:53 -08001638 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001639 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001640 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001641 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001642 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1643 if device_specific_diffs:
1644 assert all(isinstance(diff, common.BlockDifference)
1645 for diff in device_specific_diffs), \
1646 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1647 "BlockDifference objects"
1648 for diff in device_specific_diffs:
1649 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001650
Geremy Condra36bd3652014-02-06 19:45:10 -08001651 script.Comment("---- start making changes here ----")
1652
1653 device_specific.IncrementalOTA_InstallBegin()
1654
Yifan Hong10c530d2018-12-27 17:34:18 -08001655 block_diffs = [system_diff]
1656 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001657 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001658 block_diffs.append(vendor_diff)
1659 progress_dict["vendor"] = 0.1
1660 if device_specific_diffs:
1661 block_diffs += device_specific_diffs
1662
1663 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1664 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1665 raise RuntimeError(
1666 "can't generate incremental that disables dynamic partitions")
1667 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1668 info_dict=OPTIONS.target_info_dict,
1669 source_info_dict=OPTIONS.source_info_dict,
1670 block_diffs=block_diffs,
1671 progress_dict=progress_dict)
1672 dynamic_partitions_diff.WriteScript(
1673 script, output_zip, write_verify_script=OPTIONS.verify)
1674 else:
1675 for block_diff in block_diffs:
1676 block_diff.WriteScript(script, output_zip,
1677 progress=progress_dict.get(block_diff.partition),
1678 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001679
1680 if OPTIONS.two_step:
1681 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1682 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001683 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001684
1685 if not OPTIONS.two_step:
1686 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001687 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001688 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001689 script.Print("Installing boot image...")
1690 script.WriteRawImage("/boot", "boot.img")
1691 else:
1692 # Produce the boot image by applying a patch to the current
1693 # contents of the boot partition, and write it back to the
1694 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001695 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001696 script.Print("Patching boot image...")
1697 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001698 script.PatchPartition(
1699 '{}:{}:{}:{}'.format(
1700 boot_type, boot_device, target_boot.size, target_boot.sha1),
1701 '{}:{}:{}:{}'.format(
1702 boot_type, boot_device, source_boot.size, source_boot.sha1),
1703 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001704 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001705 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001706
1707 # Do device-specific installation (eg, write radio image).
1708 device_specific.IncrementalOTA_InstallEnd()
1709
1710 if OPTIONS.extra_script is not None:
1711 script.AppendExtra(OPTIONS.extra_script)
1712
Doug Zongker922206e2014-03-04 13:16:24 -08001713 if OPTIONS.wipe_user_data:
1714 script.Print("Erasing user data...")
1715 script.FormatPartition("/data")
1716
Geremy Condra36bd3652014-02-06 19:45:10 -08001717 if OPTIONS.two_step:
1718 script.AppendExtra("""
1719set_stage("%(bcb_dev)s", "");
1720endif;
1721endif;
1722""" % bcb_dev)
1723
1724 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001725 # For downgrade OTAs, we prefer to use the update-binary in the source
1726 # build that is actually newer than the one in the target build.
1727 if OPTIONS.downgrade:
1728 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1729 else:
1730 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001731 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001732
1733 # We haven't written the metadata entry yet, which will be handled in
1734 # FinalizeMetadata().
1735 common.ZipClose(output_zip)
1736
1737 # Sign the generated zip package unless no_signing is specified.
1738 needed_property_files = (
1739 NonAbOtaPropertyFiles(),
1740 )
1741 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001742
Doug Zongker32b527d2014-03-04 10:03:02 -08001743
Tao Bao15a146a2018-02-21 16:06:59 -08001744def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001745 """Returns a target-files.zip file for generating secondary payload.
1746
1747 Although the original target-files.zip already contains secondary slot
1748 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1749 ones without _other suffix. Note that we cannot instead modify the names in
1750 META/ab_partitions.txt, because there are no matching partitions on device.
1751
1752 For the partitions that don't have secondary images, the ones for primary
1753 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1754 bootloader images in the inactive slot.
1755
1756 Args:
1757 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001758 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001759
1760 Returns:
1761 The filename of the target-files.zip for generating secondary payload.
1762 """
1763 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1764 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1765
Tao Baodba59ee2018-01-09 13:21:02 -08001766 with zipfile.ZipFile(input_file, 'r') as input_zip:
1767 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001768 namelist = input_zip.namelist()
1769
1770 # Additionally unzip 'RADIO/*' if exists.
1771 unzip_pattern = UNZIP_PATTERN[:]
1772 if any([entry.startswith('RADIO/') for entry in namelist]):
1773 unzip_pattern.append('RADIO/*')
1774 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001775
1776 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001777 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1778 if info.filename == 'IMAGES/system_other.img':
1779 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1780
1781 # Primary images and friends need to be skipped explicitly.
1782 elif info.filename in ('IMAGES/system.img',
1783 'IMAGES/system.map'):
1784 pass
1785
Tao Bao15a146a2018-02-21 16:06:59 -08001786 # Skip copying the postinstall config if requested.
1787 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1788 pass
1789
Tao Bao12489802018-07-12 14:47:38 -07001790 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001791 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1792
Tao Baof7140c02018-01-30 17:09:24 -08001793 common.ZipClose(target_zip)
1794
1795 return target_file
1796
1797
Tao Bao15a146a2018-02-21 16:06:59 -08001798def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1799 """Returns a target-files.zip that's not containing postinstall_config.txt.
1800
1801 This allows brillo_update_payload script to skip writing all the postinstall
1802 hooks in the generated payload. The input target-files.zip file will be
1803 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1804 contain the postinstall_config.txt entry, the input file will be returned.
1805
1806 Args:
1807 input_file: The input target-files.zip filename.
1808
1809 Returns:
1810 The filename of target-files.zip that doesn't contain postinstall config.
1811 """
1812 # We should only make a copy if postinstall_config entry exists.
1813 with zipfile.ZipFile(input_file, 'r') as input_zip:
1814 if POSTINSTALL_CONFIG not in input_zip.namelist():
1815 return input_file
1816
1817 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1818 shutil.copyfile(input_file, target_file)
1819 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1820 return target_file
1821
1822
Yifan Hong50e79542018-11-08 17:44:12 -08001823def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
1824 super_block_devices):
1825 """Returns a target-files.zip for retrofitting dynamic partitions.
1826
1827 This allows brillo_update_payload to generate an OTA based on the exact
1828 bits on the block devices. Postinstall is disabled.
1829
1830 Args:
1831 input_file: The input target-files.zip filename.
1832 super_block_devices: The list of super block devices
1833
1834 Returns:
1835 The filename of target-files.zip with *.img replaced with super_*.img for
1836 each block device in super_block_devices.
1837 """
1838 assert super_block_devices, "No super_block_devices are specified."
1839
1840 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001841 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001842
1843 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1844 shutil.copyfile(input_file, target_file)
1845
1846 with zipfile.ZipFile(input_file, 'r') as input_zip:
1847 namelist = input_zip.namelist()
1848
1849 # Always skip postinstall for a retrofit update.
1850 to_delete = [POSTINSTALL_CONFIG]
1851
1852 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1853 # is a regular update on devices without dynamic partitions support.
1854 to_delete += [DYNAMIC_PARTITION_INFO]
1855
Tao Bao03fecb62018-11-28 10:59:23 -08001856 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001857 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001858 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001859
1860 common.ZipDelete(target_file, to_delete)
1861
1862 input_tmp = common.UnzipTemp(input_file, SUPER_SPLIT_PATTERN)
1863 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1864
1865 # Write super_{foo}.img as {foo}.img.
1866 for src, dst in replace.items():
1867 assert src in namelist, \
1868 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1869 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1870 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1871
1872 common.ZipClose(target_zip)
1873
1874 return target_file
1875
1876
Tao Baoc098e9e2016-01-07 13:03:56 -08001877def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1878 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001879 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001880 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001881 if not OPTIONS.no_signing:
1882 staging_file = common.MakeTempFile(suffix='.zip')
1883 else:
1884 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001885 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001886 compression=zipfile.ZIP_DEFLATED)
1887
Tao Bao481bab82017-12-21 11:23:09 -08001888 if source_file is not None:
1889 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1890 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1891 else:
1892 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1893 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001894
Tao Bao481bab82017-12-21 11:23:09 -08001895 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001896 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001897
Yifan Hong50e79542018-11-08 17:44:12 -08001898 if OPTIONS.retrofit_dynamic_partitions:
1899 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
1900 target_file, target_info.get("super_block_devices").strip().split())
1901 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001902 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1903
Tao Bao40b18822018-01-30 18:19:04 -08001904 # Generate payload.
1905 payload = Payload()
1906
1907 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001908 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001909 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001910 else:
1911 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001912 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001913
Tao Bao40b18822018-01-30 18:19:04 -08001914 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001915
Tao Bao40b18822018-01-30 18:19:04 -08001916 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001917 payload_signer = PayloadSigner()
1918 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001919
Tao Bao40b18822018-01-30 18:19:04 -08001920 # Write the payload into output zip.
1921 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001922
Tao Baof7140c02018-01-30 17:09:24 -08001923 # Generate and include the secondary payload that installs secondary images
1924 # (e.g. system_other.img).
1925 if OPTIONS.include_secondary:
1926 # We always include a full payload for the secondary slot, even when
1927 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001928 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1929 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001930 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001931 secondary_payload.Generate(secondary_target_file,
1932 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001933 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001934 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001935
Tianjie Xucfa86222016-03-07 16:31:19 -08001936 # If dm-verity is supported for the device, copy contents of care_map
1937 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001938 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001939 if (target_info.get("verity") == "true" or
1940 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001941 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1942 "META/" + x in target_zip.namelist()]
1943
1944 # Adds care_map if either the protobuf format or the plain text one exists.
1945 if care_map_list:
1946 care_map_name = care_map_list[0]
1947 care_map_data = target_zip.read("META/" + care_map_name)
1948 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001949 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001950 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001951 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001952 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001953 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001954
Tao Baobcd1d162017-08-26 13:10:26 -07001955 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001956 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001957
Tao Bao21803d32017-04-19 10:16:09 -07001958 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001959
Tao Baofe5b69a2018-03-02 09:47:43 -08001960 # We haven't written the metadata entry yet, which will be handled in
1961 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001962 common.ZipClose(output_zip)
1963
Tao Bao85f16982018-03-08 16:28:33 -08001964 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1965 # all the info of the latter. However, system updaters and OTA servers need to
1966 # take time to switch to the new flag. We keep both of the flags for
1967 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08001968 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08001969 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08001970 StreamingPropertyFiles(),
1971 )
1972 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08001973
Tao Baoc098e9e2016-01-07 13:03:56 -08001974
Doug Zongkereef39442009-04-02 12:14:19 -07001975def main(argv):
1976
1977 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07001978 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07001979 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001980 elif o in ("-i", "--incremental_from"):
1981 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001982 elif o == "--full_radio":
1983 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001984 elif o == "--full_bootloader":
1985 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08001986 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001987 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08001988 elif o == "--downgrade":
1989 OPTIONS.downgrade = True
1990 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08001991 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07001992 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07001993 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001994 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08001995 elif o == "--oem_no_mount":
1996 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001997 elif o in ("-e", "--extra_script"):
1998 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001999 elif o in ("-t", "--worker_threads"):
2000 if a.isdigit():
2001 OPTIONS.worker_threads = int(a)
2002 else:
2003 raise ValueError("Cannot parse value %r for option %r - only "
2004 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002005 elif o in ("-2", "--two_step"):
2006 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002007 elif o == "--include_secondary":
2008 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002009 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002010 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002011 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002012 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002013 elif o == "--block":
2014 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002015 elif o in ("-b", "--binary"):
2016 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002017 elif o == "--stash_threshold":
2018 try:
2019 OPTIONS.stash_threshold = float(a)
2020 except ValueError:
2021 raise ValueError("Cannot parse value %r for option %r - expecting "
2022 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002023 elif o == "--log_diff":
2024 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002025 elif o == "--payload_signer":
2026 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002027 elif o == "--payload_signer_args":
2028 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002029 elif o == "--extracted_input_target_files":
2030 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002031 elif o == "--skip_postinstall":
2032 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002033 elif o == "--retrofit_dynamic_partitions":
2034 OPTIONS.retrofit_dynamic_partitions = True
Doug Zongkereef39442009-04-02 12:14:19 -07002035 else:
2036 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002037 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002038
2039 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002040 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002041 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002042 "package_key=",
2043 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002044 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002045 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002046 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002047 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002048 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002049 "extra_script=",
2050 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002051 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002052 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002053 "no_signing",
2054 "block",
2055 "binary=",
2056 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002057 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002058 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002059 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002060 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002061 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002062 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002063 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002064 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002065 "retrofit_dynamic_partitions",
Dan Albert8b72aef2015-03-23 19:13:21 -07002066 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002067
2068 if len(args) != 2:
2069 common.Usage(__doc__)
2070 sys.exit(1)
2071
Tao Bao32fcdab2018-10-12 10:30:39 -07002072 common.InitLogging()
2073
Tao Bao5d182562016-02-23 11:38:39 -08002074 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002075 # We should only allow downgrading incrementals (as opposed to full).
2076 # Otherwise the device may go back from arbitrary build with this full
2077 # OTA package.
2078 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002079 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002080
Tao Bao2db13852018-01-08 22:28:57 -08002081 # Load the build info dicts from the zip directly or the extracted input
2082 # directory. We don't need to unzip the entire target-files zips, because they
2083 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2084 # When loading the info dicts, we don't need to provide the second parameter
2085 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2086 # some properties with their actual paths, such as 'selinux_fc',
2087 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002088 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002089 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002090 else:
Tao Bao2db13852018-01-08 22:28:57 -08002091 with zipfile.ZipFile(args[0], 'r') as input_zip:
2092 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002093
Tao Bao32fcdab2018-10-12 10:30:39 -07002094 logger.info("--- target info ---")
2095 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002096
2097 # Load the source build dict if applicable.
2098 if OPTIONS.incremental_source is not None:
2099 OPTIONS.target_info_dict = OPTIONS.info_dict
2100 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2101 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2102
Tao Bao32fcdab2018-10-12 10:30:39 -07002103 logger.info("--- source info ---")
2104 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002105
2106 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002107 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2108
Yifan Hong50e79542018-11-08 17:44:12 -08002109 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002110 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002111 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002112 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2113 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002114 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2115 raise common.ExternalError(
2116 "Expect to generate incremental OTA for retrofitting dynamic "
2117 "partitions, but dynamic_partition_retrofit is not set in target "
2118 "build.")
2119 logger.info("Implicitly generating retrofit incremental OTA.")
2120 OPTIONS.retrofit_dynamic_partitions = True
2121
2122 # Skip postinstall for retrofitting dynamic partitions.
2123 if OPTIONS.retrofit_dynamic_partitions:
2124 OPTIONS.skip_postinstall = True
2125
Tao Baoc098e9e2016-01-07 13:03:56 -08002126 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2127
Christian Oderf63e2cd2017-05-01 22:30:15 +02002128 # Use the default key to sign the package if not specified with package_key.
2129 # package_keys are needed on ab_updates, so always define them if an
2130 # ab_update is getting created.
2131 if not OPTIONS.no_signing or ab_update:
2132 if OPTIONS.package_key is None:
2133 OPTIONS.package_key = OPTIONS.info_dict.get(
2134 "default_system_dev_certificate",
2135 "build/target/product/security/testkey")
2136 # Get signing keys
2137 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2138
Tao Baoc098e9e2016-01-07 13:03:56 -08002139 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002140 WriteABOTAPackageWithBrilloScript(
2141 target_file=args[0],
2142 output_file=args[1],
2143 source_file=OPTIONS.incremental_source)
2144
Tao Bao32fcdab2018-10-12 10:30:39 -07002145 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002146 return
2147
Tao Bao2db13852018-01-08 22:28:57 -08002148 # Sanity check the loaded info dicts first.
2149 if OPTIONS.info_dict.get("no_recovery") == "true":
2150 raise common.ExternalError(
2151 "--- target build has specified no recovery ---")
2152
2153 # Non-A/B OTAs rely on /cache partition to store temporary files.
2154 cache_size = OPTIONS.info_dict.get("cache_size")
2155 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002156 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002157 OPTIONS.cache_size = cache_size
2158
Doug Zongker1c390a22009-05-14 19:06:36 -07002159 if OPTIONS.extra_script is not None:
2160 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2161
Dan Willemsencea5cd22017-03-21 14:44:27 -07002162 if OPTIONS.extracted_input is not None:
2163 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002164 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002165 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002166 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002167 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002168
Tao Bao2db13852018-01-08 22:28:57 -08002169 # If the caller explicitly specified the device-specific extensions path via
2170 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2171 # is present in the target target_files. Otherwise, take the path of the file
2172 # from 'tool_extensions' in the info dict and look for that in the local
2173 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002174 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002175 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2176 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002177 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002178 OPTIONS.device_specific = from_input
2179 else:
Tao Bao2db13852018-01-08 22:28:57 -08002180 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002181
Doug Zongker37974732010-09-16 17:44:38 -07002182 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002183 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002184
Tao Bao767e3ac2015-11-10 12:19:19 -08002185 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002186 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002187 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002188 WriteFullOTAPackage(
2189 input_zip,
2190 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002191
Tao Bao32b80dc2018-01-08 22:50:47 -08002192 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002193 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002194 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002195 OPTIONS.source_tmp = common.UnzipTemp(
2196 OPTIONS.incremental_source, UNZIP_PATTERN)
2197 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2198 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002199 WriteBlockIncrementalOTAPackage(
2200 input_zip,
2201 source_zip,
2202 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002203
2204 if OPTIONS.log_diff:
2205 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002206 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002207 target_files_diff.recursiveDiff(
2208 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002209
Tao Bao32fcdab2018-10-12 10:30:39 -07002210 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002211
2212
2213if __name__ == '__main__':
2214 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002215 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002216 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002217 except common.ExternalError:
2218 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002219 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002220 finally:
2221 common.Cleanup()