blob: 02d07da78b27e8c822917ee755b1c226b38cfc1b [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'
Yifan Hongb433eba2019-03-06 12:42:53 -0800229AB_PARTITIONS = 'META/ab_partitions.txt'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800230UNZIP_PATTERN = ['IMAGES/*', 'META/*']
Yifan Hongb433eba2019-03-06 12:42:53 -0800231RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
Tao Bao6b0b2f92017-03-05 11:38:11 -0800232
Tao Bao2dd1c482017-02-03 16:49:39 -0800233
Tao Bao481bab82017-12-21 11:23:09 -0800234class BuildInfo(object):
235 """A class that holds the information for a given build.
236
237 This class wraps up the property querying for a given source or target build.
238 It abstracts away the logic of handling OEM-specific properties, and caches
239 the commonly used properties such as fingerprint.
240
241 There are two types of info dicts: a) build-time info dict, which is generated
242 at build time (i.e. included in a target_files zip); b) OEM info dict that is
243 specified at package generation time (via command line argument
244 '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
245 having "oem_fingerprint_properties" in build-time info dict), all the queries
246 would be answered based on build-time info dict only. Otherwise if using
247 OEM-specific properties, some of them will be calculated from two info dicts.
248
249 Users can query properties similarly as using a dict() (e.g. info['fstab']),
250 or to query build properties via GetBuildProp() or GetVendorBuildProp().
251
252 Attributes:
253 info_dict: The build-time info dict.
254 is_ab: Whether it's a build that uses A/B OTA.
255 oem_dicts: A list of OEM dicts.
256 oem_props: A list of OEM properties that should be read from OEM dicts; None
257 if the build doesn't use any OEM-specific property.
258 fingerprint: The fingerprint of the build, which would be calculated based
259 on OEM properties if applicable.
260 device: The device name, which could come from OEM dicts if applicable.
261 """
262
Steven Laver9e73e822019-01-29 20:20:08 -0800263 _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
264 "ro.product.manufacturer", "ro.product.model",
265 "ro.product.name"]
266 _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = ["product", "product_services",
267 "odm", "vendor", "system"]
268
Tao Bao481bab82017-12-21 11:23:09 -0800269 def __init__(self, info_dict, oem_dicts):
270 """Initializes a BuildInfo instance with the given dicts.
271
Tao Bao667c7532018-07-06 10:13:59 -0700272 Note that it only wraps up the given dicts, without making copies.
273
Tao Bao481bab82017-12-21 11:23:09 -0800274 Arguments:
275 info_dict: The build-time info dict.
276 oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
277 that it always uses the first dict to calculate the fingerprint or the
278 device name. The rest would be used for asserting OEM properties only
Tao Bao667c7532018-07-06 10:13:59 -0700279 (e.g. one package can be installed on one of these devices).
Tao Bao481bab82017-12-21 11:23:09 -0800280 """
281 self.info_dict = info_dict
282 self.oem_dicts = oem_dicts
283
284 self._is_ab = info_dict.get("ab_update") == "true"
285 self._oem_props = info_dict.get("oem_fingerprint_properties")
286
287 if self._oem_props:
288 assert oem_dicts, "OEM source required for this build"
289
290 # These two should be computed only after setting self._oem_props.
291 self._device = self.GetOemProperty("ro.product.device")
292 self._fingerprint = self.CalculateFingerprint()
293
294 @property
295 def is_ab(self):
296 return self._is_ab
297
298 @property
299 def device(self):
300 return self._device
301
302 @property
303 def fingerprint(self):
304 return self._fingerprint
305
306 @property
Tao Baoea6cbd02018-09-05 13:06:37 -0700307 def vendor_fingerprint(self):
308 if "vendor.build.prop" not in self.info_dict:
309 return None
310 vendor_build_prop = self.info_dict["vendor.build.prop"]
311 if "ro.vendor.build.fingerprint" in vendor_build_prop:
312 return vendor_build_prop["ro.vendor.build.fingerprint"]
313 if "ro.vendor.build.thumbprint" in vendor_build_prop:
314 return vendor_build_prop["ro.vendor.build.thumbprint"]
315 return None
316
317 @property
Tao Bao481bab82017-12-21 11:23:09 -0800318 def oem_props(self):
319 return self._oem_props
320
321 def __getitem__(self, key):
322 return self.info_dict[key]
323
Tao Bao667c7532018-07-06 10:13:59 -0700324 def __setitem__(self, key, value):
325 self.info_dict[key] = value
326
Tao Bao481bab82017-12-21 11:23:09 -0800327 def get(self, key, default=None):
328 return self.info_dict.get(key, default)
329
Tao Bao667c7532018-07-06 10:13:59 -0700330 def items(self):
331 return self.info_dict.items()
332
Tao Bao481bab82017-12-21 11:23:09 -0800333 def GetBuildProp(self, prop):
334 """Returns the inquired build property."""
Steven Laver9e73e822019-01-29 20:20:08 -0800335 if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
336 return self._ResolveRoProductBuildProp(prop)
337
Tao Bao481bab82017-12-21 11:23:09 -0800338 try:
339 return self.info_dict.get("build.prop", {})[prop]
340 except KeyError:
341 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
342
Steven Laver9e73e822019-01-29 20:20:08 -0800343 def _ResolveRoProductBuildProp(self, prop):
344 """Resolves the inquired ro.product.* build property"""
345 prop_val = self.info_dict.get("build.prop", {}).get(prop)
346 if prop_val:
347 return prop_val
348
349 source_order_val = self.info_dict.get("build.prop", {}).get(
350 "ro.product.property_source_order")
351 if source_order_val:
352 source_order = source_order_val.split(",")
353 else:
354 source_order = BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
355
356 # Check that all sources in ro.product.property_source_order are valid
357 if any([x not in BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER
358 for x in source_order]):
359 raise common.ExternalError(
360 "Invalid ro.product.property_source_order '{}'".format(source_order))
361
362 for source in source_order:
363 source_prop = prop.replace("ro.product", "ro.product.{}".format(source),
364 1)
365 prop_val = self.info_dict.get("{}.build.prop".format(source), {}).get(
366 source_prop)
367 if prop_val:
368 return prop_val
369
370 raise common.ExternalError("couldn't resolve {}".format(prop))
371
Tao Bao481bab82017-12-21 11:23:09 -0800372 def GetVendorBuildProp(self, prop):
373 """Returns the inquired vendor build property."""
374 try:
375 return self.info_dict.get("vendor.build.prop", {})[prop]
376 except KeyError:
377 raise common.ExternalError(
378 "couldn't find %s in vendor.build.prop" % (prop,))
379
380 def GetOemProperty(self, key):
381 if self.oem_props is not None and key in self.oem_props:
382 return self.oem_dicts[0][key]
383 return self.GetBuildProp(key)
384
385 def CalculateFingerprint(self):
386 if self.oem_props is None:
Steven Laver9e73e822019-01-29 20:20:08 -0800387 try:
388 return self.GetBuildProp("ro.build.fingerprint")
389 except common.ExternalError:
390 return "{}/{}/{}:{}/{}/{}:{}/{}".format(
391 self.GetBuildProp("ro.product.brand"),
392 self.GetBuildProp("ro.product.name"),
393 self.GetBuildProp("ro.product.device"),
394 self.GetBuildProp("ro.build.version.release"),
395 self.GetBuildProp("ro.build.id"),
396 self.GetBuildProp("ro.build.version.incremental"),
397 self.GetBuildProp("ro.build.type"),
398 self.GetBuildProp("ro.build.tags"))
Tao Bao481bab82017-12-21 11:23:09 -0800399 return "%s/%s/%s:%s" % (
400 self.GetOemProperty("ro.product.brand"),
401 self.GetOemProperty("ro.product.name"),
402 self.GetOemProperty("ro.product.device"),
403 self.GetBuildProp("ro.build.thumbprint"))
404
405 def WriteMountOemScript(self, script):
406 assert self.oem_props is not None
407 recovery_mount_options = self.info_dict.get("recovery_mount_options")
408 script.Mount("/oem", recovery_mount_options)
409
410 def WriteDeviceAssertions(self, script, oem_no_mount):
411 # Read the property directly if not using OEM properties.
412 if not self.oem_props:
413 script.AssertDevice(self.device)
414 return
415
416 # Otherwise assert OEM properties.
417 if not self.oem_dicts:
418 raise common.ExternalError(
419 "No OEM file provided to answer expected assertions")
420
421 for prop in self.oem_props.split():
422 values = []
423 for oem_dict in self.oem_dicts:
424 if prop in oem_dict:
425 values.append(oem_dict[prop])
426 if not values:
427 raise common.ExternalError(
428 "The OEM file is missing the property %s" % (prop,))
429 script.AssertOemProperty(prop, values, oem_no_mount)
430
431
Tao Baofabe0832018-01-17 15:52:28 -0800432class PayloadSigner(object):
433 """A class that wraps the payload signing works.
434
435 When generating a Payload, hashes of the payload and metadata files will be
436 signed with the device key, either by calling an external payload signer or
437 by calling openssl with the package key. This class provides a unified
438 interface, so that callers can just call PayloadSigner.Sign().
439
440 If an external payload signer has been specified (OPTIONS.payload_signer), it
441 calls the signer with the provided args (OPTIONS.payload_signer_args). Note
442 that the signing key should be provided as part of the payload_signer_args.
443 Otherwise without an external signer, it uses the package key
444 (OPTIONS.package_key) and calls openssl for the signing works.
445 """
446
447 def __init__(self):
448 if OPTIONS.payload_signer is None:
449 # Prepare the payload signing key.
450 private_key = OPTIONS.package_key + OPTIONS.private_key_suffix
451 pw = OPTIONS.key_passwords[OPTIONS.package_key]
452
453 cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"]
454 cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"])
455 signing_key = common.MakeTempFile(prefix="key-", suffix=".key")
456 cmd.extend(["-out", signing_key])
Tao Baobec89c12018-10-15 11:53:28 -0700457 common.RunAndCheckOutput(cmd, verbose=False)
Tao Baofabe0832018-01-17 15:52:28 -0800458
459 self.signer = "openssl"
460 self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key,
461 "-pkeyopt", "digest:sha256"]
462 else:
463 self.signer = OPTIONS.payload_signer
464 self.signer_args = OPTIONS.payload_signer_args
465
466 def Sign(self, in_file):
467 """Signs the given input file. Returns the output filename."""
468 out_file = common.MakeTempFile(prefix="signed-", suffix=".bin")
469 cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file]
Tao Baobec89c12018-10-15 11:53:28 -0700470 common.RunAndCheckOutput(cmd)
Tao Baofabe0832018-01-17 15:52:28 -0800471 return out_file
472
473
Tao Bao40b18822018-01-30 18:19:04 -0800474class Payload(object):
475 """Manages the creation and the signing of an A/B OTA Payload."""
476
477 PAYLOAD_BIN = 'payload.bin'
478 PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tao Baof7140c02018-01-30 17:09:24 -0800479 SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin'
480 SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Tao Bao40b18822018-01-30 18:19:04 -0800481
Tao Bao667ff572018-02-10 00:02:40 -0800482 def __init__(self, secondary=False):
483 """Initializes a Payload instance.
484
485 Args:
486 secondary: Whether it's generating a secondary payload (default: False).
487 """
Tao Bao40b18822018-01-30 18:19:04 -0800488 self.payload_file = None
489 self.payload_properties = None
Tao Bao667ff572018-02-10 00:02:40 -0800490 self.secondary = secondary
Tao Bao40b18822018-01-30 18:19:04 -0800491
492 def Generate(self, target_file, source_file=None, additional_args=None):
493 """Generates a payload from the given target-files zip(s).
494
495 Args:
496 target_file: The filename of the target build target-files zip.
497 source_file: The filename of the source build target-files zip; or None if
498 generating a full OTA.
499 additional_args: A list of additional args that should be passed to
500 brillo_update_payload script; or None.
501 """
502 if additional_args is None:
503 additional_args = []
504
505 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
506 cmd = ["brillo_update_payload", "generate",
507 "--payload", payload_file,
508 "--target_image", target_file]
509 if source_file is not None:
510 cmd.extend(["--source_image", source_file])
511 cmd.extend(additional_args)
Tao Baobec89c12018-10-15 11:53:28 -0700512 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800513
514 self.payload_file = payload_file
515 self.payload_properties = None
516
517 def Sign(self, payload_signer):
518 """Generates and signs the hashes of the payload and metadata.
519
520 Args:
521 payload_signer: A PayloadSigner() instance that serves the signing work.
522
523 Raises:
524 AssertionError: On any failure when calling brillo_update_payload script.
525 """
526 assert isinstance(payload_signer, PayloadSigner)
527
528 # 1. Generate hashes of the payload and metadata files.
529 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
530 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
531 cmd = ["brillo_update_payload", "hash",
532 "--unsigned_payload", self.payload_file,
533 "--signature_size", "256",
534 "--metadata_hash_file", metadata_sig_file,
535 "--payload_hash_file", payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700536 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800537
538 # 2. Sign the hashes.
539 signed_payload_sig_file = payload_signer.Sign(payload_sig_file)
540 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
541
542 # 3. Insert the signatures back into the payload file.
543 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
544 suffix=".bin")
545 cmd = ["brillo_update_payload", "sign",
546 "--unsigned_payload", self.payload_file,
547 "--payload", signed_payload_file,
548 "--signature_size", "256",
549 "--metadata_signature_file", signed_metadata_sig_file,
550 "--payload_signature_file", signed_payload_sig_file]
Tao Baobec89c12018-10-15 11:53:28 -0700551 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800552
553 # 4. Dump the signed payload properties.
554 properties_file = common.MakeTempFile(prefix="payload-properties-",
555 suffix=".txt")
556 cmd = ["brillo_update_payload", "properties",
557 "--payload", signed_payload_file,
558 "--properties_file", properties_file]
Tao Baobec89c12018-10-15 11:53:28 -0700559 common.RunAndCheckOutput(cmd)
Tao Bao40b18822018-01-30 18:19:04 -0800560
Tao Bao667ff572018-02-10 00:02:40 -0800561 if self.secondary:
562 with open(properties_file, "a") as f:
563 f.write("SWITCH_SLOT_ON_REBOOT=0\n")
564
Tao Bao40b18822018-01-30 18:19:04 -0800565 if OPTIONS.wipe_user_data:
566 with open(properties_file, "a") as f:
567 f.write("POWERWASH=1\n")
568
569 self.payload_file = signed_payload_file
570 self.payload_properties = properties_file
571
Tao Bao667ff572018-02-10 00:02:40 -0800572 def WriteToZip(self, output_zip):
Tao Bao40b18822018-01-30 18:19:04 -0800573 """Writes the payload to the given zip.
574
575 Args:
576 output_zip: The output ZipFile instance.
577 """
578 assert self.payload_file is not None
579 assert self.payload_properties is not None
580
Tao Bao667ff572018-02-10 00:02:40 -0800581 if self.secondary:
Tao Baof7140c02018-01-30 17:09:24 -0800582 payload_arcname = Payload.SECONDARY_PAYLOAD_BIN
583 payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT
584 else:
585 payload_arcname = Payload.PAYLOAD_BIN
586 payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT
587
Tao Bao40b18822018-01-30 18:19:04 -0800588 # Add the signed payload file and properties into the zip. In order to
589 # support streaming, we pack them as ZIP_STORED. So these entries can be
590 # read directly with the offset and length pairs.
Tao Baof7140c02018-01-30 17:09:24 -0800591 common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800592 compress_type=zipfile.ZIP_STORED)
593 common.ZipWrite(output_zip, self.payload_properties,
Tao Baof7140c02018-01-30 17:09:24 -0800594 arcname=payload_properties_arcname,
Tao Bao40b18822018-01-30 18:19:04 -0800595 compress_type=zipfile.ZIP_STORED)
596
597
Doug Zongkereef39442009-04-02 12:14:19 -0700598def SignOutput(temp_zip_name, output_zip_name):
Christian Oderf63e2cd2017-05-01 22:30:15 +0200599 pw = OPTIONS.key_passwords[OPTIONS.package_key]
Doug Zongkereef39442009-04-02 12:14:19 -0700600
Doug Zongker951495f2009-08-14 12:44:19 -0700601 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
602 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700603
604
Tao Bao481bab82017-12-21 11:23:09 -0800605def _LoadOemDicts(oem_source):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800606 """Returns the list of loaded OEM properties dict."""
Tao Bao481bab82017-12-21 11:23:09 -0800607 if not oem_source:
608 return None
609
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800610 oem_dicts = []
Tao Bao481bab82017-12-21 11:23:09 -0800611 for oem_file in oem_source:
612 with open(oem_file) as fp:
613 oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines()))
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800614 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700615
Doug Zongkereef39442009-04-02 12:14:19 -0700616
Tao Baod42e97e2016-11-30 12:11:57 -0800617def _WriteRecoveryImageToBoot(script, output_zip):
618 """Find and write recovery image to /boot in two-step OTA.
619
620 In two-step OTAs, we write recovery image to /boot as the first step so that
621 we can reboot to there and install a new recovery image to /recovery.
622 A special "recovery-two-step.img" will be preferred, which encodes the correct
623 path of "/boot". Otherwise the device may show "device is corrupt" message
624 when booting into /boot.
625
626 Fall back to using the regular recovery.img if the two-step recovery image
627 doesn't exist. Note that rebuilding the special image at this point may be
628 infeasible, because we don't have the desired boot signer and keys when
629 calling ota_from_target_files.py.
630 """
631
632 recovery_two_step_img_name = "recovery-two-step.img"
633 recovery_two_step_img_path = os.path.join(
634 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
635 if os.path.exists(recovery_two_step_img_path):
636 recovery_two_step_img = common.GetBootableImage(
637 recovery_two_step_img_name, recovery_two_step_img_name,
638 OPTIONS.input_tmp, "RECOVERY")
639 common.ZipWriteStr(
640 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao32fcdab2018-10-12 10:30:39 -0700641 logger.info(
642 "two-step package: using %s in stage 1/3", recovery_two_step_img_name)
Tao Baod42e97e2016-11-30 12:11:57 -0800643 script.WriteRawImage("/boot", recovery_two_step_img_name)
644 else:
Tao Bao32fcdab2018-10-12 10:30:39 -0700645 logger.info("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800646 # The "recovery.img" entry has been written into package earlier.
647 script.WriteRawImage("/boot", "recovery.img")
648
649
Doug Zongkerc9253822014-02-04 12:17:58 -0800650def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700651 namelist = [name for name in target_files_zip.namelist()]
652 return ("SYSTEM/recovery-from-boot.p" in namelist or
653 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700654
Tao Bao457cbf62017-03-06 09:56:01 -0800655
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700656def HasVendorPartition(target_files_zip):
657 try:
658 target_files_zip.getinfo("VENDOR/")
659 return True
660 except KeyError:
661 return False
662
Tao Bao457cbf62017-03-06 09:56:01 -0800663
Tao Bao481bab82017-12-21 11:23:09 -0800664def HasTrebleEnabled(target_files_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700665 return (HasVendorPartition(target_files_zip) and
Tao Bao481bab82017-12-21 11:23:09 -0800666 target_info.GetBuildProp("ro.treble.enabled") == "true")
Tao Baobcd1d162017-08-26 13:10:26 -0700667
668
Tao Bao481bab82017-12-21 11:23:09 -0800669def WriteFingerprintAssertion(script, target_info, source_info):
670 source_oem_props = source_info.oem_props
671 target_oem_props = target_info.oem_props
Michael Runge6e836112014-04-15 17:40:21 -0700672
Tao Bao481bab82017-12-21 11:23:09 -0800673 if source_oem_props is None and target_oem_props is None:
674 script.AssertSomeFingerprint(
675 source_info.fingerprint, target_info.fingerprint)
676 elif source_oem_props is not None and target_oem_props is not None:
677 script.AssertSomeThumbprint(
678 target_info.GetBuildProp("ro.build.thumbprint"),
679 source_info.GetBuildProp("ro.build.thumbprint"))
680 elif source_oem_props is None and target_oem_props is not None:
681 script.AssertFingerprintOrThumbprint(
682 source_info.fingerprint,
683 target_info.GetBuildProp("ro.build.thumbprint"))
684 else:
685 script.AssertFingerprintOrThumbprint(
686 target_info.fingerprint,
687 source_info.GetBuildProp("ro.build.thumbprint"))
Doug Zongker73ef8252009-07-23 15:12:53 -0700688
Doug Zongkerfc44a512014-08-26 13:10:25 -0700689
Tao Bao481bab82017-12-21 11:23:09 -0800690def AddCompatibilityArchiveIfTrebleEnabled(target_zip, output_zip, target_info,
691 source_info=None):
Tao Baobcd1d162017-08-26 13:10:26 -0700692 """Adds compatibility info into the output zip if it's Treble-enabled target.
Tao Bao21803d32017-04-19 10:16:09 -0700693
694 Metadata used for on-device compatibility verification is retrieved from
695 target_zip then added to compatibility.zip which is added to the output_zip
696 archive.
697
Tao Baobcd1d162017-08-26 13:10:26 -0700698 Compatibility archive should only be included for devices that have enabled
699 Treble support.
Tao Bao21803d32017-04-19 10:16:09 -0700700
701 Args:
702 target_zip: Zip file containing the source files to be included for OTA.
703 output_zip: Zip file that will be sent for OTA.
Tao Bao481bab82017-12-21 11:23:09 -0800704 target_info: The BuildInfo instance that holds the target build info.
705 source_info: The BuildInfo instance that holds the source build info, if
706 generating an incremental OTA; None otherwise.
Tao Bao21803d32017-04-19 10:16:09 -0700707 """
708
Tao Baobcd1d162017-08-26 13:10:26 -0700709 def AddCompatibilityArchive(system_updated, vendor_updated):
710 """Adds compatibility info based on system/vendor update status.
Tao Bao21803d32017-04-19 10:16:09 -0700711
Tao Baobcd1d162017-08-26 13:10:26 -0700712 Args:
713 system_updated: If True, the system image will be updated and therefore
714 its metadata should be included.
715 vendor_updated: If True, the vendor image will be updated and therefore
716 its metadata should be included.
717 """
718 # Determine what metadata we need. Files are names relative to META/.
719 compatibility_files = []
720 vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
721 system_metadata = ("system_manifest.xml", "system_matrix.xml")
722 if vendor_updated:
723 compatibility_files += vendor_metadata
724 if system_updated:
725 compatibility_files += system_metadata
Tao Bao21803d32017-04-19 10:16:09 -0700726
Tao Baobcd1d162017-08-26 13:10:26 -0700727 # Create new archive.
728 compatibility_archive = tempfile.NamedTemporaryFile()
Tao Bao481bab82017-12-21 11:23:09 -0800729 compatibility_archive_zip = zipfile.ZipFile(
730 compatibility_archive, "w", compression=zipfile.ZIP_DEFLATED)
Tao Bao21803d32017-04-19 10:16:09 -0700731
Tao Baobcd1d162017-08-26 13:10:26 -0700732 # Add metadata.
733 for file_name in compatibility_files:
734 target_file_name = "META/" + file_name
Tao Bao21803d32017-04-19 10:16:09 -0700735
Tao Baobcd1d162017-08-26 13:10:26 -0700736 if target_file_name in target_zip.namelist():
737 data = target_zip.read(target_file_name)
738 common.ZipWriteStr(compatibility_archive_zip, file_name, data)
Tao Bao21803d32017-04-19 10:16:09 -0700739
Tao Baobcd1d162017-08-26 13:10:26 -0700740 # Ensure files are written before we copy into output_zip.
741 compatibility_archive_zip.close()
742
743 # Only add the archive if we have any compatibility info.
744 if compatibility_archive_zip.namelist():
745 common.ZipWrite(output_zip, compatibility_archive.name,
746 arcname="compatibility.zip",
747 compress_type=zipfile.ZIP_STORED)
748
749 # Will only proceed if the target has enabled the Treble support (as well as
750 # having a /vendor partition).
Tao Bao481bab82017-12-21 11:23:09 -0800751 if not HasTrebleEnabled(target_zip, target_info):
Tao Baobcd1d162017-08-26 13:10:26 -0700752 return
753
Tao Baobcd1d162017-08-26 13:10:26 -0700754 # Full OTA carries the info for system/vendor both.
Tao Bao481bab82017-12-21 11:23:09 -0800755 if source_info is None:
Tao Baobcd1d162017-08-26 13:10:26 -0700756 AddCompatibilityArchive(True, True)
757 return
758
Tao Bao481bab82017-12-21 11:23:09 -0800759 source_fp = source_info.fingerprint
760 target_fp = target_info.fingerprint
Tao Baobcd1d162017-08-26 13:10:26 -0700761 system_updated = source_fp != target_fp
762
Tao Baoea6cbd02018-09-05 13:06:37 -0700763 source_fp_vendor = source_info.vendor_fingerprint
764 target_fp_vendor = target_info.vendor_fingerprint
765 # vendor build fingerprints could be possibly blacklisted at build time. For
766 # such a case, we consider the vendor images being changed.
767 if source_fp_vendor is None or target_fp_vendor is None:
768 vendor_updated = True
769 else:
770 vendor_updated = source_fp_vendor != target_fp_vendor
Tao Baobcd1d162017-08-26 13:10:26 -0700771
772 AddCompatibilityArchive(system_updated, vendor_updated)
Tao Bao21803d32017-04-19 10:16:09 -0700773
774
Tao Bao491d7e22018-02-21 13:17:22 -0800775def WriteFullOTAPackage(input_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -0800776 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
Doug Zongkereef39442009-04-02 12:14:19 -0700777
Tao Bao481bab82017-12-21 11:23:09 -0800778 # We don't know what version it will be installed on top of. We expect the API
779 # just won't change very often. Similarly for fstab, it might have changed in
780 # the target build.
781 target_api_version = target_info["recovery_api_version"]
782 script = edify_generator.EdifyGenerator(target_api_version, target_info)
Michael Runge6e836112014-04-15 17:40:21 -0700783
Tao Bao481bab82017-12-21 11:23:09 -0800784 if target_info.oem_props and not OPTIONS.oem_no_mount:
785 target_info.WriteMountOemScript(script)
786
Tao Baodf3a48b2018-01-10 16:30:43 -0800787 metadata = GetPackageMetadata(target_info)
Doug Zongker2ea21062010-04-28 16:05:21 -0700788
Tao Bao491d7e22018-02-21 13:17:22 -0800789 if not OPTIONS.no_signing:
790 staging_file = common.MakeTempFile(suffix='.zip')
791 else:
792 staging_file = output_file
793
794 output_zip = zipfile.ZipFile(
795 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
796
Doug Zongker05d3dea2009-06-22 11:32:31 -0700797 device_specific = common.DeviceSpecificParams(
798 input_zip=input_zip,
Tao Bao481bab82017-12-21 11:23:09 -0800799 input_version=target_api_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700800 output_zip=output_zip,
801 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700802 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700803 metadata=metadata,
804 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700805
Tao Bao457cbf62017-03-06 09:56:01 -0800806 assert HasRecoveryPatch(input_zip)
Doug Zongkerc9253822014-02-04 12:17:58 -0800807
Tao Bao481bab82017-12-21 11:23:09 -0800808 # Assertions (e.g. downgrade check, device properties check).
809 ts = target_info.GetBuildProp("ro.build.date.utc")
810 ts_text = target_info.GetBuildProp("ro.build.date")
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700811 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700812
Tao Bao481bab82017-12-21 11:23:09 -0800813 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700814 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800815
816 # Two-step package strategy (in chronological order, which is *not*
817 # the order in which the generated script has things):
818 #
819 # if stage is not "2/3" or "3/3":
820 # write recovery image to boot partition
821 # set stage to "2/3"
822 # reboot to boot partition and restart recovery
823 # else if stage is "2/3":
824 # write recovery image to recovery partition
825 # set stage to "3/3"
826 # reboot to recovery partition and restart recovery
827 # else:
828 # (stage must be "3/3")
829 # set stage to ""
830 # do normal full package installation:
831 # wipe and install system, boot image, etc.
832 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700833 # complete script normally
834 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800835
836 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
837 OPTIONS.input_tmp, "RECOVERY")
838 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -0800839 if not target_info.get("multistage_support"):
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800840 assert False, "two-step packages not supported by this build"
Tao Bao481bab82017-12-21 11:23:09 -0800841 fs = target_info["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800842 assert fs.fs_type.upper() == "EMMC", \
843 "two-step packages only supported on devices with EMMC /misc partitions"
844 bcb_dev = {"bcb_dev": fs.device}
845 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
846 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700847if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800848""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800849
850 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
851 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800852 script.WriteRawImage("/recovery", "recovery.img")
853 script.AppendExtra("""
854set_stage("%(bcb_dev)s", "3/3");
855reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700856else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800857""" % bcb_dev)
858
Tao Baod42e97e2016-11-30 12:11:57 -0800859 # Stage 3/3: Make changes.
860 script.Comment("Stage 3/3")
861
Tao Bao6c55a8a2015-04-08 15:30:27 -0700862 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -0800863 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -0700864
Doug Zongkere5ff5902012-01-17 10:55:37 -0800865 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700866
Doug Zongker01ce19c2014-02-04 13:48:15 -0800867 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700868
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700869 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800870 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700871 if HasVendorPartition(input_zip):
872 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700873
Doug Zongker4b9596f2014-06-09 14:15:45 -0700874 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800875
Tao Baoe709b092018-02-07 12:40:00 -0800876 # See the notes in WriteBlockIncrementalOTAPackage().
877 allow_shared_blocks = target_info.get('ext4_share_dup_blocks') == "true"
878
Yifan Hong10c530d2018-12-27 17:34:18 -0800879 def GetBlockDifference(partition):
880 # Full OTA is done as an "incremental" against an empty source image. This
881 # has the effect of writing new data from the package to the entire
882 # partition, but lets us reuse the updater code that writes incrementals to
883 # do it.
884 tgt = common.GetSparseImage(partition, OPTIONS.input_tmp, input_zip,
885 allow_shared_blocks)
886 tgt.ResetFileMap()
887 diff = common.BlockDifference(partition, tgt, src=None)
888 return diff
Doug Zongkereef39442009-04-02 12:14:19 -0700889
Yifan Hong10c530d2018-12-27 17:34:18 -0800890 device_specific_diffs = device_specific.FullOTA_GetBlockDifferences()
891 if device_specific_diffs:
892 assert all(isinstance(diff, common.BlockDifference)
893 for diff in device_specific_diffs), \
894 "FullOTA_GetBlockDifferences is not returning a list of " \
895 "BlockDifference objects"
Doug Zongkerc9253822014-02-04 12:17:58 -0800896
Yifan Hong10c530d2018-12-27 17:34:18 -0800897 progress_dict = dict()
898 block_diffs = [GetBlockDifference("system")]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700899 if HasVendorPartition(input_zip):
Yifan Hong10c530d2018-12-27 17:34:18 -0800900 block_diffs.append(GetBlockDifference("vendor"))
901 progress_dict["vendor"] = 0.1
902 if device_specific_diffs:
903 block_diffs += device_specific_diffs
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700904
Yifan Hong10c530d2018-12-27 17:34:18 -0800905 if target_info.get('use_dynamic_partitions') == "true":
906 # Use empty source_info_dict to indicate that all partitions / groups must
907 # be re-added.
908 dynamic_partitions_diff = common.DynamicPartitionsDifference(
909 info_dict=OPTIONS.info_dict,
910 block_diffs=block_diffs,
911 progress_dict=progress_dict)
912 dynamic_partitions_diff.WriteScript(script, output_zip,
913 write_verify_script=OPTIONS.verify)
914 else:
915 for block_diff in block_diffs:
916 block_diff.WriteScript(script, output_zip,
917 progress=progress_dict.get(block_diff.partition),
918 write_verify_script=OPTIONS.verify)
Doug Zongker73ef8252009-07-23 15:12:53 -0700919
Tao Bao481bab82017-12-21 11:23:09 -0800920 AddCompatibilityArchiveIfTrebleEnabled(input_zip, output_zip, target_info)
Tao Baobcd1d162017-08-26 13:10:26 -0700921
Yifan Hong10c530d2018-12-27 17:34:18 -0800922 boot_img = common.GetBootableImage(
923 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Tao Bao481bab82017-12-21 11:23:09 -0800924 common.CheckSize(boot_img.data, "boot.img", target_info)
Doug Zongker73ef8252009-07-23 15:12:53 -0700925 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700926
Doug Zongker01ce19c2014-02-04 13:48:15 -0800927 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700928 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700929
Doug Zongker01ce19c2014-02-04 13:48:15 -0800930 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700931 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700932
Doug Zongker1c390a22009-05-14 19:06:36 -0700933 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700934 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700935
Doug Zongker14833602010-02-02 13:12:04 -0800936 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800937
Doug Zongker922206e2014-03-04 13:16:24 -0800938 if OPTIONS.wipe_user_data:
939 script.ShowProgress(0.1, 10)
940 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700941
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800942 if OPTIONS.two_step:
943 script.AppendExtra("""
944set_stage("%(bcb_dev)s", "");
945""" % bcb_dev)
946 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800947
948 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
949 script.Comment("Stage 1/3")
950 _WriteRecoveryImageToBoot(script, output_zip)
951
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800952 script.AppendExtra("""
953set_stage("%(bcb_dev)s", "2/3");
954reboot_now("%(bcb_dev)s", "");
955endif;
956endif;
957""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800958
Tao Bao5d182562016-02-23 11:38:39 -0800959 script.SetProgress(1)
960 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800961 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -0800962
963 # We haven't written the metadata entry, which will be done in
964 # FinalizeMetadata.
965 common.ZipClose(output_zip)
966
967 needed_property_files = (
968 NonAbOtaPropertyFiles(),
969 )
970 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Doug Zongker2ea21062010-04-28 16:05:21 -0700971
Doug Zongkerfc44a512014-08-26 13:10:25 -0700972
Doug Zongker2ea21062010-04-28 16:05:21 -0700973def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800974 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
975 common.ZipWriteStr(output_zip, METADATA_NAME, value,
976 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700977
Doug Zongkerfc44a512014-08-26 13:10:25 -0700978
Tao Bao481bab82017-12-21 11:23:09 -0800979def HandleDowngradeMetadata(metadata, target_info, source_info):
Tao Baob31892e2017-02-07 11:21:17 -0800980 # Only incremental OTAs are allowed to reach here.
981 assert OPTIONS.incremental_source is not None
982
Tao Bao481bab82017-12-21 11:23:09 -0800983 post_timestamp = target_info.GetBuildProp("ro.build.date.utc")
984 pre_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baob31892e2017-02-07 11:21:17 -0800985 is_downgrade = long(post_timestamp) < long(pre_timestamp)
986
987 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800988 if not is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700989 raise RuntimeError(
990 "--downgrade or --override_timestamp specified but no downgrade "
991 "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800992 metadata["ota-downgrade"] = "yes"
Tao Baob31892e2017-02-07 11:21:17 -0800993 else:
994 if is_downgrade:
Tao Baofaa8e0b2018-04-12 14:31:43 -0700995 raise RuntimeError(
996 "Downgrade detected based on timestamp check: pre: %s, post: %s. "
997 "Need to specify --override_timestamp OR --downgrade to allow "
998 "building the incremental." % (pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800999
1000
Tao Baodf3a48b2018-01-10 16:30:43 -08001001def GetPackageMetadata(target_info, source_info=None):
1002 """Generates and returns the metadata dict.
1003
1004 It generates a dict() that contains the info to be written into an OTA
1005 package (META-INF/com/android/metadata). It also handles the detection of
Tao Baofaa8e0b2018-04-12 14:31:43 -07001006 downgrade / data wipe based on the global options.
Tao Baodf3a48b2018-01-10 16:30:43 -08001007
1008 Args:
1009 target_info: The BuildInfo instance that holds the target build info.
1010 source_info: The BuildInfo instance that holds the source build info, or
1011 None if generating full OTA.
1012
1013 Returns:
1014 A dict to be written into package metadata entry.
1015 """
1016 assert isinstance(target_info, BuildInfo)
1017 assert source_info is None or isinstance(source_info, BuildInfo)
1018
1019 metadata = {
1020 'post-build' : target_info.fingerprint,
1021 'post-build-incremental' : target_info.GetBuildProp(
1022 'ro.build.version.incremental'),
Tao Bao35dc2552018-02-01 13:18:00 -08001023 'post-sdk-level' : target_info.GetBuildProp(
1024 'ro.build.version.sdk'),
1025 'post-security-patch-level' : target_info.GetBuildProp(
1026 'ro.build.version.security_patch'),
Tao Baodf3a48b2018-01-10 16:30:43 -08001027 }
1028
1029 if target_info.is_ab:
1030 metadata['ota-type'] = 'AB'
1031 metadata['ota-required-cache'] = '0'
1032 else:
1033 metadata['ota-type'] = 'BLOCK'
1034
1035 if OPTIONS.wipe_user_data:
1036 metadata['ota-wipe'] = 'yes'
1037
1038 is_incremental = source_info is not None
1039 if is_incremental:
1040 metadata['pre-build'] = source_info.fingerprint
1041 metadata['pre-build-incremental'] = source_info.GetBuildProp(
1042 'ro.build.version.incremental')
1043 metadata['pre-device'] = source_info.device
1044 else:
1045 metadata['pre-device'] = target_info.device
1046
Tao Baofaa8e0b2018-04-12 14:31:43 -07001047 # Use the actual post-timestamp, even for a downgrade case.
1048 metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc')
1049
1050 # Detect downgrades and set up downgrade flags accordingly.
Tao Baodf3a48b2018-01-10 16:30:43 -08001051 if is_incremental:
1052 HandleDowngradeMetadata(metadata, target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -08001053
1054 return metadata
1055
1056
Tao Baod3fc38a2018-03-08 16:09:01 -08001057class PropertyFiles(object):
1058 """A class that computes the property-files string for an OTA package.
1059
1060 A property-files string is a comma-separated string that contains the
1061 offset/size info for an OTA package. The entries, which must be ZIP_STORED,
1062 can be fetched directly with the package URL along with the offset/size info.
1063 These strings can be used for streaming A/B OTAs, or allowing an updater to
1064 download package metadata entry directly, without paying the cost of
1065 downloading entire package.
Tao Baofe5b69a2018-03-02 09:47:43 -08001066
Tao Baocc8e2662018-03-01 19:30:00 -08001067 Computing the final property-files string requires two passes. Because doing
1068 the whole package signing (with signapk.jar) will possibly reorder the ZIP
1069 entries, which may in turn invalidate earlier computed ZIP entry offset/size
1070 values.
1071
1072 This class provides functions to be called for each pass. The general flow is
1073 as follows.
1074
Tao Baod3fc38a2018-03-08 16:09:01 -08001075 property_files = PropertyFiles()
Tao Baocc8e2662018-03-01 19:30:00 -08001076 # The first pass, which writes placeholders before doing initial signing.
1077 property_files.Compute()
1078 SignOutput()
1079
1080 # The second pass, by replacing the placeholders with actual data.
1081 property_files.Finalize()
1082 SignOutput()
1083
1084 And the caller can additionally verify the final result.
1085
1086 property_files.Verify()
Tao Baofe5b69a2018-03-02 09:47:43 -08001087 """
1088
Tao Baocc8e2662018-03-01 19:30:00 -08001089 def __init__(self):
Tao Baod3fc38a2018-03-08 16:09:01 -08001090 self.name = None
1091 self.required = ()
1092 self.optional = ()
Tao Baofe5b69a2018-03-02 09:47:43 -08001093
Tao Baocc8e2662018-03-01 19:30:00 -08001094 def Compute(self, input_zip):
1095 """Computes and returns a property-files string with placeholders.
Tao Baofe5b69a2018-03-02 09:47:43 -08001096
Tao Baocc8e2662018-03-01 19:30:00 -08001097 We reserve extra space for the offset and size of the metadata entry itself,
1098 although we don't know the final values until the package gets signed.
Tao Baofe5b69a2018-03-02 09:47:43 -08001099
Tao Baocc8e2662018-03-01 19:30:00 -08001100 Args:
1101 input_zip: The input ZIP file.
Tao Baofe5b69a2018-03-02 09:47:43 -08001102
Tao Baocc8e2662018-03-01 19:30:00 -08001103 Returns:
1104 A string with placeholders for the metadata offset/size info, e.g.
1105 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1106 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001107 return self.GetPropertyFilesString(input_zip, reserve_space=True)
Tao Baofe5b69a2018-03-02 09:47:43 -08001108
Tao Baod2ce2ed2018-03-16 12:59:42 -07001109 class InsufficientSpaceException(Exception):
1110 pass
1111
Tao Baocc8e2662018-03-01 19:30:00 -08001112 def Finalize(self, input_zip, reserved_length):
1113 """Finalizes a property-files string with actual METADATA offset/size info.
1114
1115 The input ZIP file has been signed, with the ZIP entries in the desired
1116 place (signapk.jar will possibly reorder the ZIP entries). Now we compute
1117 the ZIP entry offsets and construct the property-files string with actual
1118 data. Note that during this process, we must pad the property-files string
1119 to the reserved length, so that the METADATA entry size remains the same.
1120 Otherwise the entries' offsets and sizes may change again.
1121
1122 Args:
1123 input_zip: The input ZIP file.
1124 reserved_length: The reserved length of the property-files string during
1125 the call to Compute(). The final string must be no more than this
1126 size.
1127
1128 Returns:
1129 A property-files string including the metadata offset/size info, e.g.
1130 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379 ".
1131
1132 Raises:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001133 InsufficientSpaceException: If the reserved length is insufficient to hold
1134 the final string.
Tao Baocc8e2662018-03-01 19:30:00 -08001135 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001136 result = self.GetPropertyFilesString(input_zip, reserve_space=False)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001137 if len(result) > reserved_length:
1138 raise self.InsufficientSpaceException(
1139 'Insufficient reserved space: reserved={}, actual={}'.format(
1140 reserved_length, len(result)))
1141
Tao Baocc8e2662018-03-01 19:30:00 -08001142 result += ' ' * (reserved_length - len(result))
1143 return result
1144
1145 def Verify(self, input_zip, expected):
1146 """Verifies the input ZIP file contains the expected property-files string.
1147
1148 Args:
1149 input_zip: The input ZIP file.
1150 expected: The property-files string that's computed from Finalize().
1151
1152 Raises:
1153 AssertionError: On finding a mismatch.
1154 """
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001155 actual = self.GetPropertyFilesString(input_zip)
Tao Baocc8e2662018-03-01 19:30:00 -08001156 assert actual == expected, \
1157 "Mismatching streaming metadata: {} vs {}.".format(actual, expected)
1158
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -07001159 def GetPropertyFilesString(self, zip_file, reserve_space=False):
1160 """
1161 Constructs the property-files string per request.
1162
1163 Args:
1164 zip_file: The input ZIP file.
1165 reserved_length: The reserved length of the property-files string.
1166
1167 Returns:
1168 A property-files string including the metadata offset/size info, e.g.
1169 "payload.bin:679:343,payload_properties.txt:378:45,metadata: ".
1170 """
Tao Baocc8e2662018-03-01 19:30:00 -08001171
1172 def ComputeEntryOffsetSize(name):
1173 """Computes the zip entry offset and size."""
1174 info = zip_file.getinfo(name)
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001175 offset = info.header_offset
1176 offset += zipfile.sizeFileHeader
1177 offset += len(info.extra) + len(info.filename)
Tao Baocc8e2662018-03-01 19:30:00 -08001178 size = info.file_size
1179 return '%s:%d:%d' % (os.path.basename(name), offset, size)
1180
1181 tokens = []
Tao Bao85f16982018-03-08 16:28:33 -08001182 tokens.extend(self._GetPrecomputed(zip_file))
Tao Baocc8e2662018-03-01 19:30:00 -08001183 for entry in self.required:
1184 tokens.append(ComputeEntryOffsetSize(entry))
1185 for entry in self.optional:
1186 if entry in zip_file.namelist():
1187 tokens.append(ComputeEntryOffsetSize(entry))
1188
1189 # 'META-INF/com/android/metadata' is required. We don't know its actual
1190 # offset and length (as well as the values for other entries). So we reserve
Tao Baod2ce2ed2018-03-16 12:59:42 -07001191 # 15-byte as a placeholder ('offset:length'), which is sufficient to cover
1192 # the space for metadata entry. Because 'offset' allows a max of 10-digit
1193 # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the
1194 # reserved space serves the metadata entry only.
Tao Baocc8e2662018-03-01 19:30:00 -08001195 if reserve_space:
Tao Baod2ce2ed2018-03-16 12:59:42 -07001196 tokens.append('metadata:' + ' ' * 15)
Tao Baocc8e2662018-03-01 19:30:00 -08001197 else:
1198 tokens.append(ComputeEntryOffsetSize(METADATA_NAME))
1199
1200 return ','.join(tokens)
Tao Baofe5b69a2018-03-02 09:47:43 -08001201
Tao Bao85f16982018-03-08 16:28:33 -08001202 def _GetPrecomputed(self, input_zip):
1203 """Computes the additional tokens to be included into the property-files.
1204
1205 This applies to tokens without actual ZIP entries, such as
1206 payload_metadadata.bin. We want to expose the offset/size to updaters, so
1207 that they can download the payload metadata directly with the info.
1208
1209 Args:
1210 input_zip: The input zip file.
1211
1212 Returns:
1213 A list of strings (tokens) to be added to the property-files string.
1214 """
1215 # pylint: disable=no-self-use
1216 # pylint: disable=unused-argument
1217 return []
1218
Tao Baofe5b69a2018-03-02 09:47:43 -08001219
Tao Baod3fc38a2018-03-08 16:09:01 -08001220class StreamingPropertyFiles(PropertyFiles):
1221 """A subclass for computing the property-files for streaming A/B OTAs."""
1222
1223 def __init__(self):
1224 super(StreamingPropertyFiles, self).__init__()
1225 self.name = 'ota-streaming-property-files'
1226 self.required = (
1227 # payload.bin and payload_properties.txt must exist.
1228 'payload.bin',
1229 'payload_properties.txt',
1230 )
1231 self.optional = (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001232 # care_map is available only if dm-verity is enabled.
1233 'care_map.pb',
Tao Baod3fc38a2018-03-08 16:09:01 -08001234 'care_map.txt',
1235 # compatibility.zip is available only if target supports Treble.
1236 'compatibility.zip',
1237 )
1238
1239
Tao Bao85f16982018-03-08 16:28:33 -08001240class AbOtaPropertyFiles(StreamingPropertyFiles):
1241 """The property-files for A/B OTA that includes payload_metadata.bin info.
1242
1243 Since P, we expose one more token (aka property-file), in addition to the ones
1244 for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'.
1245 'payload_metadata.bin' is the header part of a payload ('payload.bin'), which
1246 doesn't exist as a separate ZIP entry, but can be used to verify if the
1247 payload can be applied on the given device.
1248
1249 For backward compatibility, we keep both of the 'ota-streaming-property-files'
1250 and the newly added 'ota-property-files' in P. The new token will only be
1251 available in 'ota-property-files'.
1252 """
1253
1254 def __init__(self):
1255 super(AbOtaPropertyFiles, self).__init__()
1256 self.name = 'ota-property-files'
1257
1258 def _GetPrecomputed(self, input_zip):
1259 offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip)
1260 return ['payload_metadata.bin:{}:{}'.format(offset, size)]
1261
1262 @staticmethod
1263 def _GetPayloadMetadataOffsetAndSize(input_zip):
1264 """Computes the offset and size of the payload metadata for a given package.
1265
1266 (From system/update_engine/update_metadata.proto)
1267 A delta update file contains all the deltas needed to update a system from
1268 one specific version to another specific version. The update format is
1269 represented by this struct pseudocode:
1270
1271 struct delta_update_file {
1272 char magic[4] = "CrAU";
1273 uint64 file_format_version;
1274 uint64 manifest_size; // Size of protobuf DeltaArchiveManifest
1275
1276 // Only present if format_version > 1:
1277 uint32 metadata_signature_size;
1278
1279 // The Bzip2 compressed DeltaArchiveManifest
1280 char manifest[metadata_signature_size];
1281
1282 // The signature of the metadata (from the beginning of the payload up to
1283 // this location, not including the signature itself). This is a
1284 // serialized Signatures message.
1285 char medatada_signature_message[metadata_signature_size];
1286
1287 // Data blobs for files, no specific format. The specific offset
1288 // and length of each data blob is recorded in the DeltaArchiveManifest.
1289 struct {
1290 char data[];
1291 } blobs[];
1292
1293 // These two are not signed:
1294 uint64 payload_signatures_message_size;
1295 char payload_signatures_message[];
1296 };
1297
1298 'payload-metadata.bin' contains all the bytes from the beginning of the
1299 payload, till the end of 'medatada_signature_message'.
1300 """
1301 payload_info = input_zip.getinfo('payload.bin')
Shashikant Baviskar338856f2018-04-12 12:11:22 +09001302 payload_offset = payload_info.header_offset
1303 payload_offset += zipfile.sizeFileHeader
1304 payload_offset += len(payload_info.extra) + len(payload_info.filename)
Tao Bao85f16982018-03-08 16:28:33 -08001305 payload_size = payload_info.file_size
1306
1307 with input_zip.open('payload.bin', 'r') as payload_fp:
1308 header_bin = payload_fp.read(24)
1309
1310 # network byte order (big-endian)
1311 header = struct.unpack("!IQQL", header_bin)
1312
1313 # 'CrAU'
1314 magic = header[0]
1315 assert magic == 0x43724155, "Invalid magic: {:x}".format(magic)
1316
1317 manifest_size = header[2]
1318 metadata_signature_size = header[3]
1319 metadata_total = 24 + manifest_size + metadata_signature_size
1320 assert metadata_total < payload_size
1321
1322 return (payload_offset, metadata_total)
1323
1324
Tao Bao491d7e22018-02-21 13:17:22 -08001325class NonAbOtaPropertyFiles(PropertyFiles):
1326 """The property-files for non-A/B OTA.
1327
1328 For non-A/B OTA, the property-files string contains the info for METADATA
1329 entry, with which a system updater can be fetched the package metadata prior
1330 to downloading the entire package.
1331 """
1332
1333 def __init__(self):
1334 super(NonAbOtaPropertyFiles, self).__init__()
1335 self.name = 'ota-property-files'
1336
1337
Tao Baod3fc38a2018-03-08 16:09:01 -08001338def FinalizeMetadata(metadata, input_file, output_file, needed_property_files):
Tao Baofe5b69a2018-03-02 09:47:43 -08001339 """Finalizes the metadata and signs an A/B OTA package.
1340
1341 In order to stream an A/B OTA package, we need 'ota-streaming-property-files'
1342 that contains the offsets and sizes for the ZIP entries. An example
1343 property-files string is as follows.
1344
1345 "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379"
1346
1347 OTA server can pass down this string, in addition to the package URL, to the
1348 system update client. System update client can then fetch individual ZIP
1349 entries (ZIP_STORED) directly at the given offset of the URL.
1350
1351 Args:
1352 metadata: The metadata dict for the package.
1353 input_file: The input ZIP filename that doesn't contain the package METADATA
1354 entry yet.
1355 output_file: The final output ZIP filename.
Tao Baod3fc38a2018-03-08 16:09:01 -08001356 needed_property_files: The list of PropertyFiles' to be generated.
Tao Baofe5b69a2018-03-02 09:47:43 -08001357 """
Tao Baofe5b69a2018-03-02 09:47:43 -08001358
Tao Baod2ce2ed2018-03-16 12:59:42 -07001359 def ComputeAllPropertyFiles(input_file, needed_property_files):
1360 # Write the current metadata entry with placeholders.
1361 with zipfile.ZipFile(input_file) as input_zip:
1362 for property_files in needed_property_files:
1363 metadata[property_files.name] = property_files.Compute(input_zip)
1364 namelist = input_zip.namelist()
Tao Baofe5b69a2018-03-02 09:47:43 -08001365
Tao Baod2ce2ed2018-03-16 12:59:42 -07001366 if METADATA_NAME in namelist:
1367 common.ZipDelete(input_file, METADATA_NAME)
1368 output_zip = zipfile.ZipFile(input_file, 'a')
1369 WriteMetadata(metadata, output_zip)
1370 common.ZipClose(output_zip)
1371
1372 if OPTIONS.no_signing:
1373 return input_file
1374
Tao Bao491d7e22018-02-21 13:17:22 -08001375 prelim_signing = common.MakeTempFile(suffix='.zip')
1376 SignOutput(input_file, prelim_signing)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001377 return prelim_signing
Tao Baofe5b69a2018-03-02 09:47:43 -08001378
Tao Baod2ce2ed2018-03-16 12:59:42 -07001379 def FinalizeAllPropertyFiles(prelim_signing, needed_property_files):
1380 with zipfile.ZipFile(prelim_signing) as prelim_signing_zip:
1381 for property_files in needed_property_files:
1382 metadata[property_files.name] = property_files.Finalize(
1383 prelim_signing_zip, len(metadata[property_files.name]))
1384
1385 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP
1386 # entries, as well as padding the entry headers. We do a preliminary signing
1387 # (with an incomplete metadata entry) to allow that to happen. Then compute
1388 # the ZIP entry offsets, write back the final metadata and do the final
1389 # signing.
1390 prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files)
1391 try:
1392 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
1393 except PropertyFiles.InsufficientSpaceException:
1394 # Even with the preliminary signing, the entry orders may change
1395 # dramatically, which leads to insufficiently reserved space during the
1396 # first call to ComputeAllPropertyFiles(). In that case, we redo all the
1397 # preliminary signing works, based on the already ordered ZIP entries, to
1398 # address the issue.
1399 prelim_signing = ComputeAllPropertyFiles(
1400 prelim_signing, needed_property_files)
1401 FinalizeAllPropertyFiles(prelim_signing, needed_property_files)
Tao Baofe5b69a2018-03-02 09:47:43 -08001402
1403 # Replace the METADATA entry.
1404 common.ZipDelete(prelim_signing, METADATA_NAME)
Tao Baod2ce2ed2018-03-16 12:59:42 -07001405 output_zip = zipfile.ZipFile(prelim_signing, 'a')
Tao Baofe5b69a2018-03-02 09:47:43 -08001406 WriteMetadata(metadata, output_zip)
1407 common.ZipClose(output_zip)
1408
1409 # Re-sign the package after updating the metadata entry.
Tao Bao491d7e22018-02-21 13:17:22 -08001410 if OPTIONS.no_signing:
1411 output_file = prelim_signing
1412 else:
1413 SignOutput(prelim_signing, output_file)
Tao Baofe5b69a2018-03-02 09:47:43 -08001414
1415 # Reopen the final signed zip to double check the streaming metadata.
Tao Baod2ce2ed2018-03-16 12:59:42 -07001416 with zipfile.ZipFile(output_file) as output_zip:
Tao Baod3fc38a2018-03-08 16:09:01 -08001417 for property_files in needed_property_files:
1418 property_files.Verify(output_zip, metadata[property_files.name].strip())
Tao Baofe5b69a2018-03-02 09:47:43 -08001419
1420
Tao Bao491d7e22018-02-21 13:17:22 -08001421def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
Tao Bao481bab82017-12-21 11:23:09 -08001422 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1423 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -08001424
Tao Bao481bab82017-12-21 11:23:09 -08001425 target_api_version = target_info["recovery_api_version"]
1426 source_api_version = source_info["recovery_api_version"]
1427 if source_api_version == 0:
Tao Bao32fcdab2018-10-12 10:30:39 -07001428 logger.warning(
1429 "Generating edify script for a source that can't install it.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001430
Tao Bao481bab82017-12-21 11:23:09 -08001431 script = edify_generator.EdifyGenerator(
1432 source_api_version, target_info, fstab=source_info["fstab"])
1433
1434 if target_info.oem_props or source_info.oem_props:
1435 if not OPTIONS.oem_no_mount:
1436 source_info.WriteMountOemScript(script)
Tao Bao3806c232015-07-05 21:08:33 -07001437
Tao Baodf3a48b2018-01-10 16:30:43 -08001438 metadata = GetPackageMetadata(target_info, source_info)
Tao Bao5d182562016-02-23 11:38:39 -08001439
Tao Bao491d7e22018-02-21 13:17:22 -08001440 if not OPTIONS.no_signing:
1441 staging_file = common.MakeTempFile(suffix='.zip')
1442 else:
1443 staging_file = output_file
1444
1445 output_zip = zipfile.ZipFile(
1446 staging_file, "w", compression=zipfile.ZIP_DEFLATED)
1447
Geremy Condra36bd3652014-02-06 19:45:10 -08001448 device_specific = common.DeviceSpecificParams(
1449 source_zip=source_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001450 source_version=source_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001451 target_zip=target_zip,
Tao Bao481bab82017-12-21 11:23:09 -08001452 target_version=target_api_version,
Geremy Condra36bd3652014-02-06 19:45:10 -08001453 output_zip=output_zip,
1454 script=script,
1455 metadata=metadata,
Tao Bao481bab82017-12-21 11:23:09 -08001456 info_dict=source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001457
Geremy Condra36bd3652014-02-06 19:45:10 -08001458 source_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001459 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001460 target_boot = common.GetBootableImage(
Tao Bao481bab82017-12-21 11:23:09 -08001461 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001462 updating_boot = (not OPTIONS.two_step and
1463 (source_boot.data != target_boot.data))
1464
Geremy Condra36bd3652014-02-06 19:45:10 -08001465 target_recovery = common.GetBootableImage(
1466 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -08001467
Tao Baoe709b092018-02-07 12:40:00 -08001468 # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
1469 # shared blocks (i.e. some blocks will show up in multiple files' block
1470 # list). We can only allocate such shared blocks to the first "owner", and
1471 # disable imgdiff for all later occurrences.
1472 allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or
1473 target_info.get('ext4_share_dup_blocks') == "true")
1474 system_src = common.GetSparseImage("system", OPTIONS.source_tmp, source_zip,
1475 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001476
1477 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1478 "system", 4096, target_info)
Tao Baoe709b092018-02-07 12:40:00 -08001479 system_tgt = common.GetSparseImage("system", OPTIONS.target_tmp, target_zip,
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001480 allow_shared_blocks,
1481 hashtree_info_generator)
Tao Baodd2a5892015-03-12 12:32:37 -07001482
Tao Bao0582cb62017-12-21 11:47:01 -08001483 blockimgdiff_version = max(
Tao Bao481bab82017-12-21 11:23:09 -08001484 int(i) for i in target_info.get("blockimgdiff_versions", "1").split(","))
Tao Bao0582cb62017-12-21 11:47:01 -08001485 assert blockimgdiff_version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001486
Tao Baof8acad12016-07-07 09:09:58 -07001487 # Check the first block of the source system partition for remount R/W only
1488 # if the filesystem is ext4.
Tao Bao481bab82017-12-21 11:23:09 -08001489 system_src_partition = source_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001490 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001491 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
1492 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
1493 # b) the blocks listed in block map may not contain all the bytes for a given
1494 # file (because they're rounded to be 4K-aligned).
Tao Bao481bab82017-12-21 11:23:09 -08001495 system_tgt_partition = target_info["fstab"]["/system"]
Tao Baof8acad12016-07-07 09:09:58 -07001496 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
1497 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001498 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001499 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001500 version=blockimgdiff_version,
1501 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001502
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001503 if HasVendorPartition(target_zip):
1504 if not HasVendorPartition(source_zip):
1505 raise RuntimeError("can't generate incremental that adds /vendor")
Tao Baoe709b092018-02-07 12:40:00 -08001506 vendor_src = common.GetSparseImage("vendor", OPTIONS.source_tmp, source_zip,
1507 allow_shared_blocks)
Tianjie Xu67c7cbb2018-08-30 00:32:07 -07001508 hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator(
1509 "vendor", 4096, target_info)
1510 vendor_tgt = common.GetSparseImage(
1511 "vendor", OPTIONS.target_tmp, target_zip, allow_shared_blocks,
1512 hashtree_info_generator)
Tianjie Xufc3422a2015-12-15 11:53:59 -08001513
1514 # Check first block of vendor partition for remount R/W only if
1515 # disk type is ext4
Tao Bao481bab82017-12-21 11:23:09 -08001516 vendor_partition = source_info["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -08001517 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -07001518 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001519 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -08001520 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -07001521 version=blockimgdiff_version,
1522 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001523 else:
1524 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -08001525
Tao Baobcd1d162017-08-26 13:10:26 -07001526 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001527 target_zip, output_zip, target_info, source_info)
Tao Baobcd1d162017-08-26 13:10:26 -07001528
Tao Bao481bab82017-12-21 11:23:09 -08001529 # Assertions (e.g. device properties check).
1530 target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount)
Geremy Condra36bd3652014-02-06 19:45:10 -08001531 device_specific.IncrementalOTA_Assertions()
1532
1533 # Two-step incremental package strategy (in chronological order,
1534 # which is *not* the order in which the generated script has
1535 # things):
1536 #
1537 # if stage is not "2/3" or "3/3":
1538 # do verification on current system
1539 # write recovery image to boot partition
1540 # set stage to "2/3"
1541 # reboot to boot partition and restart recovery
1542 # else if stage is "2/3":
1543 # write recovery image to recovery partition
1544 # set stage to "3/3"
1545 # reboot to recovery partition and restart recovery
1546 # else:
1547 # (stage must be "3/3")
1548 # perform update:
1549 # patch system files, etc.
1550 # force full install of new boot image
1551 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001552 # complete script normally
1553 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001554
1555 if OPTIONS.two_step:
Tao Bao481bab82017-12-21 11:23:09 -08001556 if not source_info.get("multistage_support"):
Geremy Condra36bd3652014-02-06 19:45:10 -08001557 assert False, "two-step packages not supported by this build"
Tao Bao24604cc2018-02-01 16:25:44 -08001558 fs = source_info["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001559 assert fs.fs_type.upper() == "EMMC", \
1560 "two-step packages only supported on devices with EMMC /misc partitions"
Tao Bao481bab82017-12-21 11:23:09 -08001561 bcb_dev = {"bcb_dev" : fs.device}
Geremy Condra36bd3652014-02-06 19:45:10 -08001562 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1563 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001564if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001565""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001566
1567 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1568 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001569 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001570 script.WriteRawImage("/recovery", "recovery.img")
1571 script.AppendExtra("""
1572set_stage("%(bcb_dev)s", "3/3");
1573reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001574else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001575""" % bcb_dev)
1576
Tao Baod42e97e2016-11-30 12:11:57 -08001577 # Stage 1/3: (a) Verify the current system.
1578 script.Comment("Stage 1/3")
1579
Tao Bao6c55a8a2015-04-08 15:30:27 -07001580 # Dump fingerprints
Tao Bao481bab82017-12-21 11:23:09 -08001581 script.Print("Source: {}".format(source_info.fingerprint))
1582 script.Print("Target: {}".format(target_info.fingerprint))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001583
Geremy Condra36bd3652014-02-06 19:45:10 -08001584 script.Print("Verifying current system...")
1585
1586 device_specific.IncrementalOTA_VerifyBegin()
1587
Tao Bao481bab82017-12-21 11:23:09 -08001588 WriteFingerprintAssertion(script, target_info, source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001589
Tao Baod8d14be2016-02-04 14:26:02 -08001590 # Check the required cache size (i.e. stashed blocks).
1591 size = []
1592 if system_diff:
1593 size.append(system_diff.required_cache)
1594 if vendor_diff:
1595 size.append(vendor_diff.required_cache)
1596
Geremy Condra36bd3652014-02-06 19:45:10 -08001597 if updating_boot:
Tao Bao481bab82017-12-21 11:23:09 -08001598 boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info)
Geremy Condra36bd3652014-02-06 19:45:10 -08001599 d = common.Difference(target_boot, source_boot)
1600 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001601 if d is None:
1602 include_full_boot = True
1603 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1604 else:
1605 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001606
Tao Bao32fcdab2018-10-12 10:30:39 -07001607 logger.info(
1608 "boot target: %d source: %d diff: %d", target_boot.size,
1609 source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001610
Tao Bao51216552018-08-26 11:53:15 -07001611 common.ZipWriteStr(output_zip, "boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001612
Tao Bao51216552018-08-26 11:53:15 -07001613 script.PatchPartitionCheck(
1614 "{}:{}:{}:{}".format(
1615 boot_type, boot_device, target_boot.size, target_boot.sha1),
1616 "{}:{}:{}:{}".format(
1617 boot_type, boot_device, source_boot.size, source_boot.sha1))
1618
Tao Baod8d14be2016-02-04 14:26:02 -08001619 size.append(target_boot.size)
1620
1621 if size:
1622 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001623
1624 device_specific.IncrementalOTA_VerifyEnd()
1625
1626 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001627 # Stage 1/3: (b) Write recovery image to /boot.
1628 _WriteRecoveryImageToBoot(script, output_zip)
1629
Geremy Condra36bd3652014-02-06 19:45:10 -08001630 script.AppendExtra("""
1631set_stage("%(bcb_dev)s", "2/3");
1632reboot_now("%(bcb_dev)s", "");
1633else
1634""" % bcb_dev)
1635
Tao Baod42e97e2016-11-30 12:11:57 -08001636 # Stage 3/3: Make changes.
1637 script.Comment("Stage 3/3")
1638
Jesse Zhao75bcea02015-01-06 10:59:53 -08001639 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001640 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001641 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001642 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Yifan Hong10c530d2018-12-27 17:34:18 -08001643 device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences()
1644 if device_specific_diffs:
1645 assert all(isinstance(diff, common.BlockDifference)
1646 for diff in device_specific_diffs), \
1647 "IncrementalOTA_GetBlockDifferences is not returning a list of " \
1648 "BlockDifference objects"
1649 for diff in device_specific_diffs:
1650 diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001651
Geremy Condra36bd3652014-02-06 19:45:10 -08001652 script.Comment("---- start making changes here ----")
1653
1654 device_specific.IncrementalOTA_InstallBegin()
1655
Yifan Hong10c530d2018-12-27 17:34:18 -08001656 block_diffs = [system_diff]
1657 progress_dict = {"system": 0.8 if vendor_diff else 0.9}
Doug Zongkerfc44a512014-08-26 13:10:25 -07001658 if vendor_diff:
Yifan Hong10c530d2018-12-27 17:34:18 -08001659 block_diffs.append(vendor_diff)
1660 progress_dict["vendor"] = 0.1
1661 if device_specific_diffs:
1662 block_diffs += device_specific_diffs
1663
1664 if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true":
1665 if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true":
1666 raise RuntimeError(
1667 "can't generate incremental that disables dynamic partitions")
1668 dynamic_partitions_diff = common.DynamicPartitionsDifference(
1669 info_dict=OPTIONS.target_info_dict,
1670 source_info_dict=OPTIONS.source_info_dict,
1671 block_diffs=block_diffs,
1672 progress_dict=progress_dict)
1673 dynamic_partitions_diff.WriteScript(
1674 script, output_zip, write_verify_script=OPTIONS.verify)
1675 else:
1676 for block_diff in block_diffs:
1677 block_diff.WriteScript(script, output_zip,
1678 progress=progress_dict.get(block_diff.partition),
1679 write_verify_script=OPTIONS.verify)
Geremy Condra36bd3652014-02-06 19:45:10 -08001680
1681 if OPTIONS.two_step:
1682 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1683 script.WriteRawImage("/boot", "boot.img")
Tao Bao32fcdab2018-10-12 10:30:39 -07001684 logger.info("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001685
1686 if not OPTIONS.two_step:
1687 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001688 if include_full_boot:
Tao Bao32fcdab2018-10-12 10:30:39 -07001689 logger.info("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001690 script.Print("Installing boot image...")
1691 script.WriteRawImage("/boot", "boot.img")
1692 else:
1693 # Produce the boot image by applying a patch to the current
1694 # contents of the boot partition, and write it back to the
1695 # partition.
Tao Bao32fcdab2018-10-12 10:30:39 -07001696 logger.info("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001697 script.Print("Patching boot image...")
1698 script.ShowProgress(0.1, 10)
Tao Bao51216552018-08-26 11:53:15 -07001699 script.PatchPartition(
1700 '{}:{}:{}:{}'.format(
1701 boot_type, boot_device, target_boot.size, target_boot.sha1),
1702 '{}:{}:{}:{}'.format(
1703 boot_type, boot_device, source_boot.size, source_boot.sha1),
1704 'boot.img.p')
Geremy Condra36bd3652014-02-06 19:45:10 -08001705 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001706 logger.info("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001707
1708 # Do device-specific installation (eg, write radio image).
1709 device_specific.IncrementalOTA_InstallEnd()
1710
1711 if OPTIONS.extra_script is not None:
1712 script.AppendExtra(OPTIONS.extra_script)
1713
Doug Zongker922206e2014-03-04 13:16:24 -08001714 if OPTIONS.wipe_user_data:
1715 script.Print("Erasing user data...")
1716 script.FormatPartition("/data")
1717
Geremy Condra36bd3652014-02-06 19:45:10 -08001718 if OPTIONS.two_step:
1719 script.AppendExtra("""
1720set_stage("%(bcb_dev)s", "");
1721endif;
1722endif;
1723""" % bcb_dev)
1724
1725 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001726 # For downgrade OTAs, we prefer to use the update-binary in the source
1727 # build that is actually newer than the one in the target build.
1728 if OPTIONS.downgrade:
1729 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1730 else:
1731 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001732 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao491d7e22018-02-21 13:17:22 -08001733
1734 # We haven't written the metadata entry yet, which will be handled in
1735 # FinalizeMetadata().
1736 common.ZipClose(output_zip)
1737
1738 # Sign the generated zip package unless no_signing is specified.
1739 needed_property_files = (
1740 NonAbOtaPropertyFiles(),
1741 )
1742 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Geremy Condra36bd3652014-02-06 19:45:10 -08001743
Doug Zongker32b527d2014-03-04 10:03:02 -08001744
Tao Bao15a146a2018-02-21 16:06:59 -08001745def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
Tao Baof7140c02018-01-30 17:09:24 -08001746 """Returns a target-files.zip file for generating secondary payload.
1747
1748 Although the original target-files.zip already contains secondary slot
1749 images (i.e. IMAGES/system_other.img), we need to rename the files to the
1750 ones without _other suffix. Note that we cannot instead modify the names in
1751 META/ab_partitions.txt, because there are no matching partitions on device.
1752
1753 For the partitions that don't have secondary images, the ones for primary
1754 slot will be used. This is to ensure that we always have valid boot, vbmeta,
1755 bootloader images in the inactive slot.
1756
1757 Args:
1758 input_file: The input target-files.zip file.
Tao Bao15a146a2018-02-21 16:06:59 -08001759 skip_postinstall: Whether to skip copying the postinstall config file.
Tao Baof7140c02018-01-30 17:09:24 -08001760
1761 Returns:
1762 The filename of the target-files.zip for generating secondary payload.
1763 """
1764 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1765 target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
1766
Tao Baodba59ee2018-01-09 13:21:02 -08001767 with zipfile.ZipFile(input_file, 'r') as input_zip:
1768 infolist = input_zip.infolist()
Tao Bao12489802018-07-12 14:47:38 -07001769 namelist = input_zip.namelist()
1770
1771 # Additionally unzip 'RADIO/*' if exists.
1772 unzip_pattern = UNZIP_PATTERN[:]
1773 if any([entry.startswith('RADIO/') for entry in namelist]):
1774 unzip_pattern.append('RADIO/*')
1775 input_tmp = common.UnzipTemp(input_file, unzip_pattern)
Tao Baodba59ee2018-01-09 13:21:02 -08001776
1777 for info in infolist:
Tao Baof7140c02018-01-30 17:09:24 -08001778 unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
1779 if info.filename == 'IMAGES/system_other.img':
1780 common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
1781
1782 # Primary images and friends need to be skipped explicitly.
1783 elif info.filename in ('IMAGES/system.img',
1784 'IMAGES/system.map'):
1785 pass
1786
Tao Bao15a146a2018-02-21 16:06:59 -08001787 # Skip copying the postinstall config if requested.
1788 elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
1789 pass
1790
Tao Bao12489802018-07-12 14:47:38 -07001791 elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
Tao Baof7140c02018-01-30 17:09:24 -08001792 common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
1793
Tao Baof7140c02018-01-30 17:09:24 -08001794 common.ZipClose(target_zip)
1795
1796 return target_file
1797
1798
Tao Bao15a146a2018-02-21 16:06:59 -08001799def GetTargetFilesZipWithoutPostinstallConfig(input_file):
1800 """Returns a target-files.zip that's not containing postinstall_config.txt.
1801
1802 This allows brillo_update_payload script to skip writing all the postinstall
1803 hooks in the generated payload. The input target-files.zip file will be
1804 duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
1805 contain the postinstall_config.txt entry, the input file will be returned.
1806
1807 Args:
1808 input_file: The input target-files.zip filename.
1809
1810 Returns:
1811 The filename of target-files.zip that doesn't contain postinstall config.
1812 """
1813 # We should only make a copy if postinstall_config entry exists.
1814 with zipfile.ZipFile(input_file, 'r') as input_zip:
1815 if POSTINSTALL_CONFIG not in input_zip.namelist():
1816 return input_file
1817
1818 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1819 shutil.copyfile(input_file, target_file)
1820 common.ZipDelete(target_file, POSTINSTALL_CONFIG)
1821 return target_file
1822
1823
Yifan Hong50e79542018-11-08 17:44:12 -08001824def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
Yifan Hongb433eba2019-03-06 12:42:53 -08001825 super_block_devices,
1826 dynamic_partition_list):
Yifan Hong50e79542018-11-08 17:44:12 -08001827 """Returns a target-files.zip for retrofitting dynamic partitions.
1828
1829 This allows brillo_update_payload to generate an OTA based on the exact
1830 bits on the block devices. Postinstall is disabled.
1831
1832 Args:
1833 input_file: The input target-files.zip filename.
1834 super_block_devices: The list of super block devices
Yifan Hongb433eba2019-03-06 12:42:53 -08001835 dynamic_partition_list: The list of dynamic partitions
Yifan Hong50e79542018-11-08 17:44:12 -08001836
1837 Returns:
1838 The filename of target-files.zip with *.img replaced with super_*.img for
1839 each block device in super_block_devices.
1840 """
1841 assert super_block_devices, "No super_block_devices are specified."
1842
1843 replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
Tao Bao03fecb62018-11-28 10:59:23 -08001844 for dev in super_block_devices}
Yifan Hong50e79542018-11-08 17:44:12 -08001845
1846 target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
1847 shutil.copyfile(input_file, target_file)
1848
1849 with zipfile.ZipFile(input_file, 'r') as input_zip:
1850 namelist = input_zip.namelist()
1851
Yifan Hongb433eba2019-03-06 12:42:53 -08001852 input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
1853
1854 # Remove partitions from META/ab_partitions.txt that is in
1855 # dynamic_partition_list but not in super_block_devices so that
1856 # brillo_update_payload won't generate update for those logical partitions.
1857 ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
1858 with open(ab_partitions_file) as f:
1859 ab_partitions_lines = f.readlines()
1860 ab_partitions = [line.strip() for line in ab_partitions_lines]
1861 # Assert that all super_block_devices are in ab_partitions
1862 super_device_not_updated = [partition for partition in super_block_devices
1863 if partition not in ab_partitions]
1864 assert not super_device_not_updated, \
1865 "{} is in super_block_devices but not in {}".format(
1866 super_device_not_updated, AB_PARTITIONS)
1867 # ab_partitions -= (dynamic_partition_list - super_block_devices)
1868 new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt")
1869 with open(new_ab_partitions, 'w') as f:
1870 for partition in ab_partitions:
1871 if (partition in dynamic_partition_list and
1872 partition not in super_block_devices):
1873 logger.info("Dropping %s from ab_partitions.txt", partition)
1874 continue
1875 f.write(partition + "\n")
1876 to_delete = [AB_PARTITIONS]
1877
Yifan Hong50e79542018-11-08 17:44:12 -08001878 # Always skip postinstall for a retrofit update.
Yifan Hongb433eba2019-03-06 12:42:53 -08001879 to_delete += [POSTINSTALL_CONFIG]
Yifan Hong50e79542018-11-08 17:44:12 -08001880
1881 # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
1882 # is a regular update on devices without dynamic partitions support.
1883 to_delete += [DYNAMIC_PARTITION_INFO]
1884
Tao Bao03fecb62018-11-28 10:59:23 -08001885 # Remove the existing partition images as well as the map files.
Yifan Hong50e79542018-11-08 17:44:12 -08001886 to_delete += replace.values()
Tao Bao03fecb62018-11-28 10:59:23 -08001887 to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
Yifan Hong50e79542018-11-08 17:44:12 -08001888
1889 common.ZipDelete(target_file, to_delete)
1890
Yifan Hong50e79542018-11-08 17:44:12 -08001891 target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
1892
1893 # Write super_{foo}.img as {foo}.img.
1894 for src, dst in replace.items():
1895 assert src in namelist, \
1896 'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
1897 unzipped_file = os.path.join(input_tmp, *src.split('/'))
1898 common.ZipWrite(target_zip, unzipped_file, arcname=dst)
1899
Yifan Hongb433eba2019-03-06 12:42:53 -08001900 # Write new ab_partitions.txt file
1901 common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
1902
Yifan Hong50e79542018-11-08 17:44:12 -08001903 common.ZipClose(target_zip)
1904
1905 return target_file
1906
1907
Tao Baoc098e9e2016-01-07 13:03:56 -08001908def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1909 source_file=None):
Tao Baofe5b69a2018-03-02 09:47:43 -08001910 """Generates an Android OTA package that has A/B update payload."""
Tao Baodea0f8b2016-06-20 17:55:06 -07001911 # Stage the output zip package for package signing.
Tao Bao491d7e22018-02-21 13:17:22 -08001912 if not OPTIONS.no_signing:
1913 staging_file = common.MakeTempFile(suffix='.zip')
1914 else:
1915 staging_file = output_file
Tao Baoa652c002018-03-01 19:31:38 -08001916 output_zip = zipfile.ZipFile(staging_file, "w",
Tao Baoc098e9e2016-01-07 13:03:56 -08001917 compression=zipfile.ZIP_DEFLATED)
1918
Tao Bao481bab82017-12-21 11:23:09 -08001919 if source_file is not None:
1920 target_info = BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
1921 source_info = BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
1922 else:
1923 target_info = BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
1924 source_info = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001925
Tao Bao481bab82017-12-21 11:23:09 -08001926 # Metadata to comply with Android OTA package format.
Tao Baodf3a48b2018-01-10 16:30:43 -08001927 metadata = GetPackageMetadata(target_info, source_info)
Tao Baob31892e2017-02-07 11:21:17 -08001928
Yifan Hong50e79542018-11-08 17:44:12 -08001929 if OPTIONS.retrofit_dynamic_partitions:
1930 target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
Yifan Hongb433eba2019-03-06 12:42:53 -08001931 target_file, target_info.get("super_block_devices").strip().split(),
1932 target_info.get("dynamic_partition_list").strip().split())
Yifan Hong50e79542018-11-08 17:44:12 -08001933 elif OPTIONS.skip_postinstall:
Tao Bao15a146a2018-02-21 16:06:59 -08001934 target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
1935
Tao Bao40b18822018-01-30 18:19:04 -08001936 # Generate payload.
1937 payload = Payload()
1938
1939 # Enforce a max timestamp this payload can be applied on top of.
Tao Baoff1b86e2017-10-03 14:17:57 -07001940 if OPTIONS.downgrade:
Tao Bao2a12ed72018-01-22 11:35:00 -08001941 max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
Tao Baoff1b86e2017-10-03 14:17:57 -07001942 else:
1943 max_timestamp = metadata["post-timestamp"]
Tao Bao40b18822018-01-30 18:19:04 -08001944 additional_args = ["--max_timestamp", max_timestamp]
Tao Baoc098e9e2016-01-07 13:03:56 -08001945
Tao Bao40b18822018-01-30 18:19:04 -08001946 payload.Generate(target_file, source_file, additional_args)
Tao Baoc098e9e2016-01-07 13:03:56 -08001947
Tao Bao40b18822018-01-30 18:19:04 -08001948 # Sign the payload.
Tao Baof7140c02018-01-30 17:09:24 -08001949 payload_signer = PayloadSigner()
1950 payload.Sign(payload_signer)
Tao Baoc098e9e2016-01-07 13:03:56 -08001951
Tao Bao40b18822018-01-30 18:19:04 -08001952 # Write the payload into output zip.
1953 payload.WriteToZip(output_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08001954
Tao Baof7140c02018-01-30 17:09:24 -08001955 # Generate and include the secondary payload that installs secondary images
1956 # (e.g. system_other.img).
1957 if OPTIONS.include_secondary:
1958 # We always include a full payload for the secondary slot, even when
1959 # building an incremental OTA. See the comments for "--include_secondary".
Tao Bao15a146a2018-02-21 16:06:59 -08001960 secondary_target_file = GetTargetFilesZipForSecondaryImages(
1961 target_file, OPTIONS.skip_postinstall)
Tao Bao667ff572018-02-10 00:02:40 -08001962 secondary_payload = Payload(secondary=True)
Tao Baodb1fe412018-02-09 23:15:05 -08001963 secondary_payload.Generate(secondary_target_file,
1964 additional_args=additional_args)
Tao Baof7140c02018-01-30 17:09:24 -08001965 secondary_payload.Sign(payload_signer)
Tao Bao667ff572018-02-10 00:02:40 -08001966 secondary_payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001967
Tianjie Xucfa86222016-03-07 16:31:19 -08001968 # If dm-verity is supported for the device, copy contents of care_map
1969 # into A/B OTA package.
Tao Bao21803d32017-04-19 10:16:09 -07001970 target_zip = zipfile.ZipFile(target_file, "r")
Tao Bao481bab82017-12-21 11:23:09 -08001971 if (target_info.get("verity") == "true" or
1972 target_info.get("avb_enable") == "true"):
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001973 care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if
1974 "META/" + x in target_zip.namelist()]
1975
1976 # Adds care_map if either the protobuf format or the plain text one exists.
1977 if care_map_list:
1978 care_map_name = care_map_list[0]
1979 care_map_data = target_zip.read("META/" + care_map_name)
1980 # In order to support streaming, care_map needs to be packed as
Tao Bao40b18822018-01-30 18:19:04 -08001981 # ZIP_STORED.
Tianjie Xu4c05f4a2018-09-14 16:24:41 -07001982 common.ZipWriteStr(output_zip, care_map_name, care_map_data,
Tao Bao481bab82017-12-21 11:23:09 -08001983 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001984 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07001985 logger.warning("Cannot find care map file in target_file package")
Tao Bao21803d32017-04-19 10:16:09 -07001986
Tao Baobcd1d162017-08-26 13:10:26 -07001987 AddCompatibilityArchiveIfTrebleEnabled(
Tao Bao481bab82017-12-21 11:23:09 -08001988 target_zip, output_zip, target_info, source_info)
Tao Bao21803d32017-04-19 10:16:09 -07001989
Tao Bao21803d32017-04-19 10:16:09 -07001990 common.ZipClose(target_zip)
Tianjie Xucfa86222016-03-07 16:31:19 -08001991
Tao Baofe5b69a2018-03-02 09:47:43 -08001992 # We haven't written the metadata entry yet, which will be handled in
1993 # FinalizeMetadata().
Tao Baoc96316c2017-01-24 22:10:49 -08001994 common.ZipClose(output_zip)
1995
Tao Bao85f16982018-03-08 16:28:33 -08001996 # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers
1997 # all the info of the latter. However, system updaters and OTA servers need to
1998 # take time to switch to the new flag. We keep both of the flags for
1999 # P-timeframe, and will remove StreamingPropertyFiles in later release.
Tao Baod3fc38a2018-03-08 16:09:01 -08002000 needed_property_files = (
Tao Bao85f16982018-03-08 16:28:33 -08002001 AbOtaPropertyFiles(),
Tao Baod3fc38a2018-03-08 16:09:01 -08002002 StreamingPropertyFiles(),
2003 )
2004 FinalizeMetadata(metadata, staging_file, output_file, needed_property_files)
Tao Baoc96316c2017-01-24 22:10:49 -08002005
Tao Baoc098e9e2016-01-07 13:03:56 -08002006
Doug Zongkereef39442009-04-02 12:14:19 -07002007def main(argv):
2008
2009 def option_handler(o, a):
Tao Bao4b76a0e2017-10-31 12:13:33 -07002010 if o in ("-k", "--package_key"):
Doug Zongkereef39442009-04-02 12:14:19 -07002011 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002012 elif o in ("-i", "--incremental_from"):
2013 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002014 elif o == "--full_radio":
2015 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002016 elif o == "--full_bootloader":
2017 OPTIONS.full_bootloader = True
Tao Bao337633f2017-12-06 15:20:19 -08002018 elif o == "--wipe_user_data":
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002019 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002020 elif o == "--downgrade":
2021 OPTIONS.downgrade = True
2022 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002023 elif o == "--override_timestamp":
Tao Baofaa8e0b2018-04-12 14:31:43 -07002024 OPTIONS.downgrade = True
Michael Runge6e836112014-04-15 17:40:21 -07002025 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002026 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002027 elif o == "--oem_no_mount":
2028 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002029 elif o in ("-e", "--extra_script"):
2030 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002031 elif o in ("-t", "--worker_threads"):
2032 if a.isdigit():
2033 OPTIONS.worker_threads = int(a)
2034 else:
2035 raise ValueError("Cannot parse value %r for option %r - only "
2036 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002037 elif o in ("-2", "--two_step"):
2038 OPTIONS.two_step = True
Tao Baof7140c02018-01-30 17:09:24 -08002039 elif o == "--include_secondary":
2040 OPTIONS.include_secondary = True
Doug Zongker26e66192014-02-20 13:22:07 -08002041 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002042 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002043 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002044 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002045 elif o == "--block":
2046 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002047 elif o in ("-b", "--binary"):
2048 OPTIONS.updater_binary = a
Tao Bao8dcf7382015-05-21 14:09:49 -07002049 elif o == "--stash_threshold":
2050 try:
2051 OPTIONS.stash_threshold = float(a)
2052 except ValueError:
2053 raise ValueError("Cannot parse value %r for option %r - expecting "
2054 "a float" % (a, o))
Tao Baod62c6032015-11-30 09:40:20 -08002055 elif o == "--log_diff":
2056 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002057 elif o == "--payload_signer":
2058 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002059 elif o == "--payload_signer_args":
2060 OPTIONS.payload_signer_args = shlex.split(a)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002061 elif o == "--extracted_input_target_files":
2062 OPTIONS.extracted_input = a
Tao Bao15a146a2018-02-21 16:06:59 -08002063 elif o == "--skip_postinstall":
2064 OPTIONS.skip_postinstall = True
Yifan Hong50e79542018-11-08 17:44:12 -08002065 elif o == "--retrofit_dynamic_partitions":
2066 OPTIONS.retrofit_dynamic_partitions = True
Doug Zongkereef39442009-04-02 12:14:19 -07002067 else:
2068 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002069 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002070
2071 args = common.ParseOptions(argv, __doc__,
Tao Bao337633f2017-12-06 15:20:19 -08002072 extra_opts="b:k:i:d:e:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002073 extra_long_opts=[
Dan Albert8b72aef2015-03-23 19:13:21 -07002074 "package_key=",
2075 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002076 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002077 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002078 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002079 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002080 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002081 "extra_script=",
2082 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002083 "two_step",
Tao Baof7140c02018-01-30 17:09:24 -08002084 "include_secondary",
Dan Albert8b72aef2015-03-23 19:13:21 -07002085 "no_signing",
2086 "block",
2087 "binary=",
2088 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002089 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002090 "verify",
Tao Bao8dcf7382015-05-21 14:09:49 -07002091 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002092 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002093 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002094 "payload_signer_args=",
Dan Willemsencea5cd22017-03-21 14:44:27 -07002095 "extracted_input_target_files=",
Tao Bao15a146a2018-02-21 16:06:59 -08002096 "skip_postinstall",
Yifan Hong50e79542018-11-08 17:44:12 -08002097 "retrofit_dynamic_partitions",
Dan Albert8b72aef2015-03-23 19:13:21 -07002098 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002099
2100 if len(args) != 2:
2101 common.Usage(__doc__)
2102 sys.exit(1)
2103
Tao Bao32fcdab2018-10-12 10:30:39 -07002104 common.InitLogging()
2105
Tao Bao5d182562016-02-23 11:38:39 -08002106 if OPTIONS.downgrade:
Tao Bao5d182562016-02-23 11:38:39 -08002107 # We should only allow downgrading incrementals (as opposed to full).
2108 # Otherwise the device may go back from arbitrary build with this full
2109 # OTA package.
2110 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002111 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002112
Tao Bao2db13852018-01-08 22:28:57 -08002113 # Load the build info dicts from the zip directly or the extracted input
2114 # directory. We don't need to unzip the entire target-files zips, because they
2115 # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
2116 # When loading the info dicts, we don't need to provide the second parameter
2117 # to common.LoadInfoDict(). Specifying the second parameter allows replacing
2118 # some properties with their actual paths, such as 'selinux_fc',
2119 # 'ramdisk_dir', which won't be used during OTA generation.
Dan Willemsencea5cd22017-03-21 14:44:27 -07002120 if OPTIONS.extracted_input is not None:
Tao Bao2db13852018-01-08 22:28:57 -08002121 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
Dan Willemsencea5cd22017-03-21 14:44:27 -07002122 else:
Tao Bao2db13852018-01-08 22:28:57 -08002123 with zipfile.ZipFile(args[0], 'r') as input_zip:
2124 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Tao Baoc098e9e2016-01-07 13:03:56 -08002125
Tao Bao32fcdab2018-10-12 10:30:39 -07002126 logger.info("--- target info ---")
2127 common.DumpInfoDict(OPTIONS.info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002128
2129 # Load the source build dict if applicable.
2130 if OPTIONS.incremental_source is not None:
2131 OPTIONS.target_info_dict = OPTIONS.info_dict
2132 with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
2133 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2134
Tao Bao32fcdab2018-10-12 10:30:39 -07002135 logger.info("--- source info ---")
2136 common.DumpInfoDict(OPTIONS.source_info_dict)
Tao Bao2db13852018-01-08 22:28:57 -08002137
2138 # Load OEM dicts if provided.
Tao Bao481bab82017-12-21 11:23:09 -08002139 OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
2140
Yifan Hong50e79542018-11-08 17:44:12 -08002141 # Assume retrofitting dynamic partitions when base build does not set
Yifan Hong50611032018-11-20 14:27:38 -08002142 # use_dynamic_partitions but target build does.
Yifan Hong50e79542018-11-08 17:44:12 -08002143 if (OPTIONS.source_info_dict and
Yifan Hong50611032018-11-20 14:27:38 -08002144 OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
2145 OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
Yifan Hong50e79542018-11-08 17:44:12 -08002146 if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
2147 raise common.ExternalError(
2148 "Expect to generate incremental OTA for retrofitting dynamic "
2149 "partitions, but dynamic_partition_retrofit is not set in target "
2150 "build.")
2151 logger.info("Implicitly generating retrofit incremental OTA.")
2152 OPTIONS.retrofit_dynamic_partitions = True
2153
2154 # Skip postinstall for retrofitting dynamic partitions.
2155 if OPTIONS.retrofit_dynamic_partitions:
2156 OPTIONS.skip_postinstall = True
2157
Tao Baoc098e9e2016-01-07 13:03:56 -08002158 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2159
Christian Oderf63e2cd2017-05-01 22:30:15 +02002160 # Use the default key to sign the package if not specified with package_key.
2161 # package_keys are needed on ab_updates, so always define them if an
2162 # ab_update is getting created.
2163 if not OPTIONS.no_signing or ab_update:
2164 if OPTIONS.package_key is None:
2165 OPTIONS.package_key = OPTIONS.info_dict.get(
2166 "default_system_dev_certificate",
2167 "build/target/product/security/testkey")
2168 # Get signing keys
2169 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
2170
Tao Baoc098e9e2016-01-07 13:03:56 -08002171 if ab_update:
Tao Baoc098e9e2016-01-07 13:03:56 -08002172 WriteABOTAPackageWithBrilloScript(
2173 target_file=args[0],
2174 output_file=args[1],
2175 source_file=OPTIONS.incremental_source)
2176
Tao Bao32fcdab2018-10-12 10:30:39 -07002177 logger.info("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002178 return
2179
Tao Bao2db13852018-01-08 22:28:57 -08002180 # Sanity check the loaded info dicts first.
2181 if OPTIONS.info_dict.get("no_recovery") == "true":
2182 raise common.ExternalError(
2183 "--- target build has specified no recovery ---")
2184
2185 # Non-A/B OTAs rely on /cache partition to store temporary files.
2186 cache_size = OPTIONS.info_dict.get("cache_size")
2187 if cache_size is None:
Tao Bao32fcdab2018-10-12 10:30:39 -07002188 logger.warning("--- can't determine the cache partition size ---")
Tao Bao2db13852018-01-08 22:28:57 -08002189 OPTIONS.cache_size = cache_size
2190
Doug Zongker1c390a22009-05-14 19:06:36 -07002191 if OPTIONS.extra_script is not None:
2192 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2193
Dan Willemsencea5cd22017-03-21 14:44:27 -07002194 if OPTIONS.extracted_input is not None:
2195 OPTIONS.input_tmp = OPTIONS.extracted_input
Dan Willemsencea5cd22017-03-21 14:44:27 -07002196 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002197 logger.info("unzipping target target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002198 OPTIONS.input_tmp = common.UnzipTemp(args[0], UNZIP_PATTERN)
Tao Bao2db13852018-01-08 22:28:57 -08002199 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002200
Tao Bao2db13852018-01-08 22:28:57 -08002201 # If the caller explicitly specified the device-specific extensions path via
2202 # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it
2203 # is present in the target target_files. Otherwise, take the path of the file
2204 # from 'tool_extensions' in the info dict and look for that in the local
2205 # filesystem, relative to the current directory.
Doug Zongker37974732010-09-16 17:44:38 -07002206 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002207 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2208 if os.path.exists(from_input):
Tao Bao32fcdab2018-10-12 10:30:39 -07002209 logger.info("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002210 OPTIONS.device_specific = from_input
2211 else:
Tao Bao2db13852018-01-08 22:28:57 -08002212 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002213
Doug Zongker37974732010-09-16 17:44:38 -07002214 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002215 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002216
Tao Bao767e3ac2015-11-10 12:19:19 -08002217 # Generate a full OTA.
Tao Bao0c6a4142017-12-15 10:11:19 -08002218 if OPTIONS.incremental_source is None:
Tao Baodba59ee2018-01-09 13:21:02 -08002219 with zipfile.ZipFile(args[0], 'r') as input_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002220 WriteFullOTAPackage(
2221 input_zip,
2222 output_file=args[1])
Tao Bao767e3ac2015-11-10 12:19:19 -08002223
Tao Bao32b80dc2018-01-08 22:50:47 -08002224 # Generate an incremental OTA.
Tao Bao767e3ac2015-11-10 12:19:19 -08002225 else:
Tao Bao32fcdab2018-10-12 10:30:39 -07002226 logger.info("unzipping source target-files...")
Tao Baodba59ee2018-01-09 13:21:02 -08002227 OPTIONS.source_tmp = common.UnzipTemp(
2228 OPTIONS.incremental_source, UNZIP_PATTERN)
2229 with zipfile.ZipFile(args[0], 'r') as input_zip, \
2230 zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip:
Tao Bao491d7e22018-02-21 13:17:22 -08002231 WriteBlockIncrementalOTAPackage(
2232 input_zip,
2233 source_zip,
2234 output_file=args[1])
Tao Bao32b80dc2018-01-08 22:50:47 -08002235
2236 if OPTIONS.log_diff:
2237 with open(OPTIONS.log_diff, 'w') as out_file:
Tao Baod62c6032015-11-30 09:40:20 -08002238 import target_files_diff
Tao Bao32b80dc2018-01-08 22:50:47 -08002239 target_files_diff.recursiveDiff(
2240 '', OPTIONS.source_tmp, OPTIONS.input_tmp, out_file)
Doug Zongker62d4f182014-08-04 16:06:43 -07002241
Tao Bao32fcdab2018-10-12 10:30:39 -07002242 logger.info("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002243
2244
2245if __name__ == '__main__':
2246 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002247 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002248 main(sys.argv[1:])
Tao Bao32fcdab2018-10-12 10:30:39 -07002249 except common.ExternalError:
2250 logger.exception("\n ERROR:\n")
Doug Zongkereef39442009-04-02 12:14:19 -07002251 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002252 finally:
2253 common.Cleanup()