blob: 8e625ffe172988332728f4e24cb8b7c49c1c01bf [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -080053 -o (--oem_settings) <main_file[,additional_files...]>
54 Comma seperated list of files used to specify the expected OEM-specific
55 properties on the OEM partition of the intended device.
56 Multiple expected values can be used by providing multiple files.
57
Tao Bao8608cde2016-02-25 19:49:55 -080058 --oem_no_mount
59 For devices with OEM-specific properties but without an OEM partition,
60 do not mount the OEM partition in the updater-script. This should be
61 very rarely used, since it's expected to have a dedicated OEM partition
62 for OEM-specific properties. Only meaningful when -o is specified.
63
Doug Zongkerdbfaae52009-04-21 17:12:54 -070064 -w (--wipe_user_data)
65 Generate an OTA package that will wipe the user data partition
66 when installed.
67
Tao Bao5d182562016-02-23 11:38:39 -080068 --downgrade
69 Intentionally generate an incremental OTA that updates from a newer
70 build to an older one (based on timestamp comparison). "post-timestamp"
71 will be replaced by "ota-downgrade=yes" in the metadata file. A data
72 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Bao4996cf02016-03-08 17:53:39 -080073 the metadata file. The update-binary in the source build will be used in
Tao Bao3e6161a2017-02-28 11:48:48 -080074 the OTA package, unless --binary flag is specified. Please also check the
75 doc for --override_timestamp below.
76
77 --override_timestamp
78 Intentionally generate an incremental OTA that updates from a newer
79 build to an older one (based on timestamp comparison), by overriding the
80 timestamp in package metadata. This differs from --downgrade flag: we
81 know for sure this is NOT an actual downgrade case, but two builds are
82 cut in a reverse order. A legit use case is that we cut a new build C
83 (after having A and B), but want to enfore an update path of A -> C -> B.
84 Specifying --downgrade may not help since that would enforce a data wipe
85 for C -> B update. The value of "post-timestamp" will be set to the newer
86 timestamp plus one, so that the package can be pushed and applied.
Tao Bao5d182562016-02-23 11:38:39 -080087
Doug Zongker1c390a22009-05-14 19:06:36 -070088 -e (--extra_script) <file>
89 Insert the contents of file at the end of the update script.
90
Doug Zongker9b23f2c2013-11-25 14:44:12 -080091 -2 (--two_step)
92 Generate a 'two-step' OTA package, where recovery is updated
93 first, so that any changes made to the system partition are done
94 using the new recovery (new kernel, etc.).
95
Doug Zongker26e66192014-02-20 13:22:07 -080096 --block
97 Generate a block-based OTA if possible. Will fall back to a
98 file-based OTA if the target_files is older and doesn't support
99 block-based OTAs.
100
Doug Zongker25568482014-03-03 10:21:27 -0800101 -b (--binary) <file>
102 Use the given binary as the update-binary in the output package,
103 instead of the binary in the build's target_files. Use for
104 development only.
105
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200106 -t (--worker_threads) <int>
107 Specifies the number of worker-threads that will be used when
108 generating patches for incremental updates (defaults to 3).
109
Tao Bao8dcf7382015-05-21 14:09:49 -0700110 --stash_threshold <float>
111 Specifies the threshold that will be used to compute the maximum
112 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800113
114 --gen_verify
115 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800116
117 --log_diff <file>
118 Generate a log file that shows the differences in the source and target
119 builds for an incremental package. This option is only meaningful when
120 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700121
122 --payload_signer <signer>
123 Specify the signer when signing the payload and metadata for A/B OTAs.
124 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
125 with the package private key. If the private key cannot be accessed
126 directly, a payload signer that knows how to do that should be specified.
127 The signer will be supplied with "-inkey <path_to_key>",
128 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700129
130 --payload_signer_args <args>
131 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700132"""
133
Tao Bao89fbb0f2017-01-10 10:47:58 -0800134from __future__ import print_function
135
Doug Zongkereef39442009-04-02 12:14:19 -0700136import sys
137
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800138if sys.hexversion < 0x02070000:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800139 print("Python 2.7 or newer is required.", file=sys.stderr)
Doug Zongkereef39442009-04-02 12:14:19 -0700140 sys.exit(1)
141
Tao Bao2dd1c482017-02-03 16:49:39 -0800142import copy
Doug Zongkerfc44a512014-08-26 13:10:25 -0700143import multiprocessing
Tao Bao2dd1c482017-02-03 16:49:39 -0800144import os.path
Tao Baoc098e9e2016-01-07 13:03:56 -0800145import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700146import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700147import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700148import zipfile
149
150import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700151import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700152import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700153
154OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700155OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700156OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700157OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700158OPTIONS.require_verbatim = set()
159OPTIONS.prohibit_verbatim = set(("system/build.prop",))
160OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700161OPTIONS.wipe_user_data = False
Tao Bao5d182562016-02-23 11:38:39 -0800162OPTIONS.downgrade = False
Tao Bao3e6161a2017-02-28 11:48:48 -0800163OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700164OPTIONS.extra_script = None
Doug Zongkerfc44a512014-08-26 13:10:25 -0700165OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
166if OPTIONS.worker_threads == 0:
167 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800168OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900169OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800170OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800171OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700172OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800173OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700174OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700175OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700176OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700177# Stash size cannot exceed cache_size * threshold.
178OPTIONS.cache_size = None
179OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800180OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800181OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700182OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700183OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700184
Tao Bao2dd1c482017-02-03 16:49:39 -0800185METADATA_NAME = 'META-INF/com/android/metadata'
Tao Bao6b0b2f92017-03-05 11:38:11 -0800186UNZIP_PATTERN = ['IMAGES/*', 'META/*']
187
Tao Bao2dd1c482017-02-03 16:49:39 -0800188
Doug Zongkereef39442009-04-02 12:14:19 -0700189def MostPopularKey(d, default):
190 """Given a dict, return the key corresponding to the largest
191 value. Returns 'default' if the dict is empty."""
192 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700193 if not x:
194 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700195 x.sort()
196 return x[-1][1]
197
198
199def IsSymlink(info):
200 """Return true if the zipfile.ZipInfo object passed in represents a
201 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700202 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700203
Hristo Bojinov96be7202010-08-02 10:26:17 -0700204def IsRegular(info):
205 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700206 regular file."""
207 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700208
Michael Runge4038aa82013-12-13 18:06:28 -0800209def ClosestFileMatch(src, tgtfiles, existing):
210 """Returns the closest file match between a source file and list
211 of potential matches. The exact filename match is preferred,
212 then the sha1 is searched for, and finally a file with the same
213 basename is evaluated. Rename support in the updater-binary is
214 required for the latter checks to be used."""
215
216 result = tgtfiles.get("path:" + src.name)
217 if result is not None:
218 return result
219
220 if not OPTIONS.target_info_dict.get("update_rename_support", False):
221 return None
222
223 if src.size < 1000:
224 return None
225
226 result = tgtfiles.get("sha1:" + src.sha1)
227 if result is not None and existing.get(result.name) is None:
228 return result
229 result = tgtfiles.get("file:" + src.name.split("/")[-1])
230 if result is not None and existing.get(result.name) is None:
231 return result
232 return None
233
Dan Albert8b72aef2015-03-23 19:13:21 -0700234class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700235 def __init__(self, partition, fs_config):
236 self.partition = partition
237 self.fs_config = fs_config
238 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700239
Dan Albert8b72aef2015-03-23 19:13:21 -0700240 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700241 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700242 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700243 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700244
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700245 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700246 # The target_files contains a record of what the uid,
247 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700248 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700249
250 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700251 if not line:
252 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700253 columns = line.split()
254 name, uid, gid, mode = columns[:4]
255 selabel = None
256 capabilities = None
257
258 # After the first 4 columns, there are a series of key=value
259 # pairs. Extract out the fields we care about.
260 for element in columns[4:]:
261 key, value = element.split("=")
262 if key == "selabel":
263 selabel = value
264 if key == "capabilities":
265 capabilities = value
266
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700267 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700268 if i is not None:
269 i.uid = int(uid)
270 i.gid = int(gid)
271 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700272 i.selabel = selabel
273 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700274 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700275 i.children.sort(key=lambda i: i.name)
276
Tao Baof2cffbd2015-07-22 12:33:18 -0700277 # Set metadata for the files generated by this script. For full recovery
278 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700279 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700280 if i:
281 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700282 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700283 if i:
284 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700285
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700286
Dan Albert8b72aef2015-03-23 19:13:21 -0700287class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700288 """Items represent the metadata (user, group, mode) of files and
289 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700290 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700291 self.itemset = itemset
292 self.name = name
293 self.uid = None
294 self.gid = None
295 self.mode = None
296 self.selabel = None
297 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700298 self.is_dir = is_dir
299 self.descendants = None
300 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700301
302 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700303 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700304 self.parent.children.append(self)
305 else:
306 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700307 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700308 self.children = []
309
310 def Dump(self, indent=0):
311 if self.uid is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800312 print("%s%s %d %d %o" % (
313 " " * indent, self.name, self.uid, self.gid, self.mode))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700314 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800315 print("%s%s %s %s %s" % (
316 " " * indent, self.name, self.uid, self.gid, self.mode))
Dan Albert8b72aef2015-03-23 19:13:21 -0700317 if self.is_dir:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800318 print("%s%s" % (" " * indent, self.descendants))
319 print("%s%s" % (" " * indent, self.best_subtree))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700320 for i in self.children:
321 i.Dump(indent=indent+1)
322
Doug Zongkereef39442009-04-02 12:14:19 -0700323 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700324 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700325 all children and determine the best strategy for using set_perm_recursive
326 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700327 values. Recursively calls itself for all descendants.
328
Dan Albert8b72aef2015-03-23 19:13:21 -0700329 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
330 counting up all descendants of this node. (dmode or fmode may be None.)
331 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
332 fmode, selabel, capabilities) tuple that will match the most descendants of
333 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700334 """
335
Dan Albert8b72aef2015-03-23 19:13:21 -0700336 assert self.is_dir
337 key = (self.uid, self.gid, self.mode, None, self.selabel,
338 self.capabilities)
339 self.descendants = {key: 1}
340 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700341 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700342 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700343 for k, v in i.CountChildMetadata().iteritems():
344 d[k] = d.get(k, 0) + v
345 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700346 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700347 d[k] = d.get(k, 0) + 1
348
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700349 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
350 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700351
352 # First, find the (uid, gid) pair that matches the most
353 # descendants.
354 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700355 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700356 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
357 ug = MostPopularKey(ug, (0, 0))
358
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700359 # Now find the dmode, fmode, selabel, and capabilities that match
360 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700361 best_dmode = (0, 0o755)
362 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700363 best_selabel = (0, None)
364 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700365 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700366 if k[:2] != ug:
367 continue
368 if k[2] is not None and count >= best_dmode[0]:
369 best_dmode = (count, k[2])
370 if k[3] is not None and count >= best_fmode[0]:
371 best_fmode = (count, k[3])
372 if k[4] is not None and count >= best_selabel[0]:
373 best_selabel = (count, k[4])
374 if k[5] is not None and count >= best_capabilities[0]:
375 best_capabilities = (count, k[5])
376 self.best_subtree = ug + (
377 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700378
379 return d
380
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700381 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700382 """Append set_perm/set_perm_recursive commands to 'script' to
383 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700384 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700385
386 self.CountChildMetadata()
387
388 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700389 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
390 # that the current item (and all its children) have already been set to.
391 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700392 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700393 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700394 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700395 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700396 current = item.best_subtree
397
398 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700399 item.mode != current[2] or item.selabel != current[4] or \
400 item.capabilities != current[5]:
401 script.SetPermissions("/"+item.name, item.uid, item.gid,
402 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700403
404 for i in item.children:
405 recurse(i, current)
406 else:
407 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700408 item.mode != current[3] or item.selabel != current[4] or \
409 item.capabilities != current[5]:
410 script.SetPermissions("/"+item.name, item.uid, item.gid,
411 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700412
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700413 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700414
415
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700416def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
417 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700418 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800419 list of symlinks. output_zip may be None, in which case the copy is
420 skipped (but the other side effects still happen). substitute is an
421 optional dict of {output filename: contents} to be output instead of
422 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700423 """
424
425 symlinks = []
426
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700427 partition = itemset.partition
428
Doug Zongkereef39442009-04-02 12:14:19 -0700429 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700430 prefix = partition.upper() + "/"
431 if info.filename.startswith(prefix):
432 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700433 if IsSymlink(info):
434 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700435 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700436 else:
437 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700438 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700439 if substitute and fn in substitute and substitute[fn] is None:
440 continue
441 if output_zip is not None:
442 if substitute and fn in substitute:
443 data = substitute[fn]
444 else:
445 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700446 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700447 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700448 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700449 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700450 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700451
452 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800453 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700454
455
Doug Zongkereef39442009-04-02 12:14:19 -0700456def SignOutput(temp_zip_name, output_zip_name):
457 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
458 pw = key_passwords[OPTIONS.package_key]
459
Doug Zongker951495f2009-08-14 12:44:19 -0700460 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
461 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700462
463
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800464def AppendAssertions(script, info_dict, oem_dicts=None):
Michael Runge6e836112014-04-15 17:40:21 -0700465 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700466 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700467 device = GetBuildProp("ro.product.device", info_dict)
468 script.AssertDevice(device)
469 else:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800470 if not oem_dicts:
Dan Albert8b72aef2015-03-23 19:13:21 -0700471 raise common.ExternalError(
472 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700473 for prop in oem_props.split():
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800474 values = []
475 for oem_dict in oem_dicts:
476 if oem_dict.get(prop):
477 values.append(oem_dict[prop])
478 if not values:
Dan Albert8b72aef2015-03-23 19:13:21 -0700479 raise common.ExternalError(
480 "The OEM file is missing the property %s" % prop)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800481 script.AssertOemProperty(prop, values)
482
483
Tao Baoebce6972017-03-06 10:22:20 -0800484def _LoadOemDicts(script, recovery_mount_options=None):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800485 """Returns the list of loaded OEM properties dict."""
486 oem_dicts = None
487 if OPTIONS.oem_source is None:
488 raise common.ExternalError("OEM source required for this build")
Tao Baoebce6972017-03-06 10:22:20 -0800489 if not OPTIONS.oem_no_mount and script:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800490 script.Mount("/oem", recovery_mount_options)
491 oem_dicts = []
492 for oem_file in OPTIONS.oem_source:
493 oem_dicts.append(common.LoadDictionaryFromLines(
494 open(oem_file).readlines()))
495 return oem_dicts
Doug Zongkereef39442009-04-02 12:14:19 -0700496
Doug Zongkereef39442009-04-02 12:14:19 -0700497
Tao Baod42e97e2016-11-30 12:11:57 -0800498def _WriteRecoveryImageToBoot(script, output_zip):
499 """Find and write recovery image to /boot in two-step OTA.
500
501 In two-step OTAs, we write recovery image to /boot as the first step so that
502 we can reboot to there and install a new recovery image to /recovery.
503 A special "recovery-two-step.img" will be preferred, which encodes the correct
504 path of "/boot". Otherwise the device may show "device is corrupt" message
505 when booting into /boot.
506
507 Fall back to using the regular recovery.img if the two-step recovery image
508 doesn't exist. Note that rebuilding the special image at this point may be
509 infeasible, because we don't have the desired boot signer and keys when
510 calling ota_from_target_files.py.
511 """
512
513 recovery_two_step_img_name = "recovery-two-step.img"
514 recovery_two_step_img_path = os.path.join(
515 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
516 if os.path.exists(recovery_two_step_img_path):
517 recovery_two_step_img = common.GetBootableImage(
518 recovery_two_step_img_name, recovery_two_step_img_name,
519 OPTIONS.input_tmp, "RECOVERY")
520 common.ZipWriteStr(
521 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800522 print("two-step package: using %s in stage 1/3" % (
523 recovery_two_step_img_name,))
Tao Baod42e97e2016-11-30 12:11:57 -0800524 script.WriteRawImage("/boot", recovery_two_step_img_name)
525 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800526 print("two-step package: using recovery.img in stage 1/3")
Tao Baod42e97e2016-11-30 12:11:57 -0800527 # The "recovery.img" entry has been written into package earlier.
528 script.WriteRawImage("/boot", "recovery.img")
529
530
Doug Zongkerc9253822014-02-04 12:17:58 -0800531def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700532 namelist = [name for name in target_files_zip.namelist()]
533 return ("SYSTEM/recovery-from-boot.p" in namelist or
534 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700535
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700536def HasVendorPartition(target_files_zip):
537 try:
538 target_files_zip.getinfo("VENDOR/")
539 return True
540 except KeyError:
541 return False
542
Michael Runge6e836112014-04-15 17:40:21 -0700543def GetOemProperty(name, oem_props, oem_dict, info_dict):
544 if oem_props is not None and name in oem_props:
545 return oem_dict[name]
546 return GetBuildProp(name, info_dict)
547
548
549def CalculateFingerprint(oem_props, oem_dict, info_dict):
550 if oem_props is None:
551 return GetBuildProp("ro.build.fingerprint", info_dict)
552 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700553 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
554 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
555 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
556 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700557
Doug Zongkerfc44a512014-08-26 13:10:25 -0700558
Doug Zongker3c84f562014-07-31 11:06:30 -0700559def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700560 # Return an image object (suitable for passing to BlockImageDiff)
561 # for the 'which' partition (most be "system" or "vendor"). If a
562 # prebuilt image and file map are found in tmpdir they are used,
563 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700564
565 assert which in ("system", "vendor")
566
567 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700568 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
569 if os.path.exists(path) and os.path.exists(mappath):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800570 print("using %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700571 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700572
573 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800574 print("building %s.img from target-files" % (which,))
Doug Zongker3c84f562014-07-31 11:06:30 -0700575
576 # This is an 'old' target-files, which does not contain images
577 # already built. Build them.
578
Doug Zongkerfc44a512014-08-26 13:10:25 -0700579 mappath = tempfile.mkstemp()[1]
580 OPTIONS.tempfiles.append(mappath)
581
Doug Zongker3c84f562014-07-31 11:06:30 -0700582 import add_img_to_target_files
583 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700584 path = add_img_to_target_files.BuildSystem(
585 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700586 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700587 path = add_img_to_target_files.BuildVendor(
588 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700589
Tao Baoff777812015-05-12 11:42:31 -0700590 # Bug: http://b/20939131
591 # In ext4 filesystems, block 0 might be changed even being mounted
592 # R/O. We add it to clobbered_blocks so that it will be written to the
593 # target unconditionally. Note that they are still part of care_map.
594 clobbered_blocks = "0"
595
596 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700597
598
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700599def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700600 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700601 # be installed on top of. For now, we expect the API just won't
602 # change very often. Similarly for fstab, it might have changed
603 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700604 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700605
Tao Bao838c68f2016-03-15 19:16:18 +0000606 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700607 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800608 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -0700609 if oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800610 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700611
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800612 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
613 OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700614 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800615 "pre-device": GetOemProperty("ro.product.device", oem_props,
616 oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -0700617 OPTIONS.info_dict),
618 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
619 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700620
Doug Zongker05d3dea2009-06-22 11:32:31 -0700621 device_specific = common.DeviceSpecificParams(
622 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700623 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700624 output_zip=output_zip,
625 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700626 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700627 metadata=metadata,
628 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700629
Doug Zongkerc9253822014-02-04 12:17:58 -0800630 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800631 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800632
Tao Baod8d14be2016-02-04 14:26:02 -0800633 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
634
Elliott Hughesd8a52f92016-06-20 14:35:47 -0700635 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
636 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
637 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700638
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800639 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700640 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800641
642 # Two-step package strategy (in chronological order, which is *not*
643 # the order in which the generated script has things):
644 #
645 # if stage is not "2/3" or "3/3":
646 # write recovery image to boot partition
647 # set stage to "2/3"
648 # reboot to boot partition and restart recovery
649 # else if stage is "2/3":
650 # write recovery image to recovery partition
651 # set stage to "3/3"
652 # reboot to recovery partition and restart recovery
653 # else:
654 # (stage must be "3/3")
655 # set stage to ""
656 # do normal full package installation:
657 # wipe and install system, boot image, etc.
658 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700659 # complete script normally
660 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800661
662 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
663 OPTIONS.input_tmp, "RECOVERY")
664 if OPTIONS.two_step:
665 if not OPTIONS.info_dict.get("multistage_support", None):
666 assert False, "two-step packages not supported by this build"
667 fs = OPTIONS.info_dict["fstab"]["/misc"]
668 assert fs.fs_type.upper() == "EMMC", \
669 "two-step packages only supported on devices with EMMC /misc partitions"
670 bcb_dev = {"bcb_dev": fs.device}
671 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
672 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700673if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800674""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -0800675
676 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
677 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800678 script.WriteRawImage("/recovery", "recovery.img")
679 script.AppendExtra("""
680set_stage("%(bcb_dev)s", "3/3");
681reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700682else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800683""" % bcb_dev)
684
Tao Baod42e97e2016-11-30 12:11:57 -0800685 # Stage 3/3: Make changes.
686 script.Comment("Stage 3/3")
687
Tao Bao6c55a8a2015-04-08 15:30:27 -0700688 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700689 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700690
Doug Zongkere5ff5902012-01-17 10:55:37 -0800691 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700692
Doug Zongker01ce19c2014-02-04 13:48:15 -0800693 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700694
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700695 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800696 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700697 if HasVendorPartition(input_zip):
698 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700699
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400700 # Place a copy of file_contexts.bin into the OTA package which will be used
701 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700702 if "selinux_fc" in OPTIONS.info_dict:
703 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500704
Michael Runge7cd99ba2014-10-22 17:21:48 -0700705 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
706
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700707 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700708 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800709
Doug Zongker26e66192014-02-20 13:22:07 -0800710 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700711 # Full OTA is done as an "incremental" against an empty source
712 # image. This has the effect of writing new data from the package
713 # to the entire partition, but lets us reuse the updater code that
714 # writes incrementals to do it.
715 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
716 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700717 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700718 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800719 else:
720 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700721 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800722 if not has_recovery_patch:
723 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800724 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700725
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700726 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800727 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700728
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700729 boot_img = common.GetBootableImage(
730 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800731
Doug Zongker91a99c22014-05-09 13:15:01 -0700732 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800733 def output_sink(fn, data):
734 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700735 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800736
737 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
738 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700739
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700740 system_items.GetMetadata(input_zip)
741 system_items.Get("system").SetPermissions(script)
742
743 if HasVendorPartition(input_zip):
744 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
745 script.ShowProgress(0.1, 0)
746
747 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700748 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
749 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700750 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700751 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700752 else:
753 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700754 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700755 script.UnpackPackageDir("vendor", "/vendor")
756
757 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
758 script.MakeSymlinks(symlinks)
759
760 vendor_items.GetMetadata(input_zip)
761 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700762
Doug Zongker37974732010-09-16 17:44:38 -0700763 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700764 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700765
Doug Zongker01ce19c2014-02-04 13:48:15 -0800766 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700767 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700768
Doug Zongker01ce19c2014-02-04 13:48:15 -0800769 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700770 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700771
Doug Zongker1c390a22009-05-14 19:06:36 -0700772 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700773 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700774
Doug Zongker14833602010-02-02 13:12:04 -0800775 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800776
Doug Zongker922206e2014-03-04 13:16:24 -0800777 if OPTIONS.wipe_user_data:
778 script.ShowProgress(0.1, 10)
779 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700780
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800781 if OPTIONS.two_step:
782 script.AppendExtra("""
783set_stage("%(bcb_dev)s", "");
784""" % bcb_dev)
785 script.AppendExtra("else\n")
Tao Baod42e97e2016-11-30 12:11:57 -0800786
787 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
788 script.Comment("Stage 1/3")
789 _WriteRecoveryImageToBoot(script, output_zip)
790
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800791 script.AppendExtra("""
792set_stage("%(bcb_dev)s", "2/3");
793reboot_now("%(bcb_dev)s", "");
794endif;
795endif;
796""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800797
Tao Bao5d182562016-02-23 11:38:39 -0800798 script.SetProgress(1)
799 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800800 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700801 WriteMetadata(metadata, output_zip)
802
Doug Zongkerfc44a512014-08-26 13:10:25 -0700803
Dan Albert8e0178d2015-01-27 15:53:15 -0800804def WritePolicyConfig(file_name, output_zip):
805 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500806
Doug Zongker2ea21062010-04-28 16:05:21 -0700807
808def WriteMetadata(metadata, output_zip):
Tao Bao2dd1c482017-02-03 16:49:39 -0800809 value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
810 common.ZipWriteStr(output_zip, METADATA_NAME, value,
811 compress_type=zipfile.ZIP_STORED)
Doug Zongkereef39442009-04-02 12:14:19 -0700812
Doug Zongkerfc44a512014-08-26 13:10:25 -0700813
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700814def LoadPartitionFiles(z, partition):
815 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700816 ZipFile, and return a dict of {filename: File object}."""
817 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700818 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700819 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700820 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700821 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700822 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700823 data = z.read(info.filename)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +0900824 out[fn] = common.File(fn, data, info.compress_size)
Doug Zongker1807e702012-02-28 12:21:08 -0800825 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700826
827
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700828def GetBuildProp(prop, info_dict):
829 """Return the fingerprint of the build of a given target-files info_dict."""
830 try:
831 return info_dict.get("build.prop", {})[prop]
832 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700833 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700834
Doug Zongkerfc44a512014-08-26 13:10:25 -0700835
Michael Runge4038aa82013-12-13 18:06:28 -0800836def AddToKnownPaths(filename, known_paths):
837 if filename[-1] == "/":
838 return
839 dirs = filename.split("/")[:-1]
840 while len(dirs) > 0:
841 path = "/".join(dirs)
842 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700843 break
Michael Runge4038aa82013-12-13 18:06:28 -0800844 known_paths.add(path)
845 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700846
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700847
Tao Baob31892e2017-02-07 11:21:17 -0800848def HandleDowngradeMetadata(metadata):
849 # Only incremental OTAs are allowed to reach here.
850 assert OPTIONS.incremental_source is not None
851
852 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
853 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
854 is_downgrade = long(post_timestamp) < long(pre_timestamp)
855
856 if OPTIONS.downgrade:
Tao Baob31892e2017-02-07 11:21:17 -0800857 if not is_downgrade:
858 raise RuntimeError("--downgrade specified but no downgrade detected: "
859 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao3e6161a2017-02-28 11:48:48 -0800860 metadata["ota-downgrade"] = "yes"
861 elif OPTIONS.timestamp:
862 if not is_downgrade:
863 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
864 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
865 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Baob31892e2017-02-07 11:21:17 -0800866 else:
867 if is_downgrade:
Tao Bao3e6161a2017-02-28 11:48:48 -0800868 raise RuntimeError("Downgrade detected based on timestamp check: "
869 "pre: %s, post: %s. Need to specify --timestamp OR "
870 "--downgrade to allow building the incremental." % (
871 pre_timestamp, post_timestamp))
Tao Baob31892e2017-02-07 11:21:17 -0800872 metadata["post-timestamp"] = post_timestamp
873
874
Geremy Condra36bd3652014-02-06 19:45:10 -0800875def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700876 # TODO(tbao): We should factor out the common parts between
877 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800878 source_version = OPTIONS.source_info_dict["recovery_api_version"]
879 target_version = OPTIONS.target_info_dict["recovery_api_version"]
880
881 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700882 print("WARNING: generating edify script for a source that "
883 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700884 script = edify_generator.EdifyGenerator(
885 source_version, OPTIONS.target_info_dict,
886 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800887
Tao Bao3806c232015-07-05 21:08:33 -0700888 recovery_mount_options = OPTIONS.source_info_dict.get(
889 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700890 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
891 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800892 oem_dicts = None
893 if source_oem_props and target_oem_props:
894 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700895
Dan Albert8b72aef2015-03-23 19:13:21 -0700896 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700897 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800898 oem_dicts and oem_dicts[0],
899 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800900 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700901 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800902
Tao Baob31892e2017-02-07 11:21:17 -0800903 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -0800904
Geremy Condra36bd3652014-02-06 19:45:10 -0800905 device_specific = common.DeviceSpecificParams(
906 source_zip=source_zip,
907 source_version=source_version,
908 target_zip=target_zip,
909 target_version=target_version,
910 output_zip=output_zip,
911 script=script,
912 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700913 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800914
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800915 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700916 OPTIONS.source_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800917 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Tao Bao3806c232015-07-05 21:08:33 -0700918 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800919 metadata["pre-build"] = source_fp
920 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700921 metadata["pre-build-incremental"] = GetBuildProp(
922 "ro.build.version.incremental", OPTIONS.source_info_dict)
923 metadata["post-build-incremental"] = GetBuildProp(
924 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800925
926 source_boot = common.GetBootableImage(
927 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
928 OPTIONS.source_info_dict)
929 target_boot = common.GetBootableImage(
930 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
931 updating_boot = (not OPTIONS.two_step and
932 (source_boot.data != target_boot.data))
933
Geremy Condra36bd3652014-02-06 19:45:10 -0800934 target_recovery = common.GetBootableImage(
935 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800936
Doug Zongkerfc44a512014-08-26 13:10:25 -0700937 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
938 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700939
940 blockimgdiff_version = 1
941 if OPTIONS.info_dict:
942 blockimgdiff_version = max(
943 int(i) for i in
944 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
945
Tao Baof8acad12016-07-07 09:09:58 -0700946 # Check the first block of the source system partition for remount R/W only
947 # if the filesystem is ext4.
948 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
949 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700950 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
951 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
952 # b) the blocks listed in block map may not contain all the bytes for a given
953 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700954 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
955 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
956 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700957 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800958 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700959 version=blockimgdiff_version,
960 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700961
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700962 if HasVendorPartition(target_zip):
963 if not HasVendorPartition(source_zip):
964 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700965 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
966 OPTIONS.source_info_dict)
967 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
968 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800969
970 # Check first block of vendor partition for remount R/W only if
971 # disk type is ext4
972 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800973 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700974 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700975 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800976 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700977 version=blockimgdiff_version,
978 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700979 else:
980 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800981
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -0800982 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Geremy Condra36bd3652014-02-06 19:45:10 -0800983 device_specific.IncrementalOTA_Assertions()
984
985 # Two-step incremental package strategy (in chronological order,
986 # which is *not* the order in which the generated script has
987 # things):
988 #
989 # if stage is not "2/3" or "3/3":
990 # do verification on current system
991 # write recovery image to boot partition
992 # set stage to "2/3"
993 # reboot to boot partition and restart recovery
994 # else if stage is "2/3":
995 # write recovery image to recovery partition
996 # set stage to "3/3"
997 # reboot to recovery partition and restart recovery
998 # else:
999 # (stage must be "3/3")
1000 # perform update:
1001 # patch system files, etc.
1002 # force full install of new boot image
1003 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001004 # complete script normally
1005 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001006
1007 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001008 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -08001009 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001010 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001011 assert fs.fs_type.upper() == "EMMC", \
1012 "two-step packages only supported on devices with EMMC /misc partitions"
1013 bcb_dev = {"bcb_dev": fs.device}
1014 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1015 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001016if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001017""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001018
1019 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1020 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001021 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001022 script.WriteRawImage("/recovery", "recovery.img")
1023 script.AppendExtra("""
1024set_stage("%(bcb_dev)s", "3/3");
1025reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001026else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001027""" % bcb_dev)
1028
Tao Baod42e97e2016-11-30 12:11:57 -08001029 # Stage 1/3: (a) Verify the current system.
1030 script.Comment("Stage 1/3")
1031
Tao Bao6c55a8a2015-04-08 15:30:27 -07001032 # Dump fingerprints
Tao Baof9023852016-12-14 11:53:38 -08001033 script.Print("Source: %s" % (source_fp,))
1034 script.Print("Target: %s" % (target_fp,))
Tao Bao6c55a8a2015-04-08 15:30:27 -07001035
Geremy Condra36bd3652014-02-06 19:45:10 -08001036 script.Print("Verifying current system...")
1037
1038 device_specific.IncrementalOTA_VerifyBegin()
1039
Tao Bao3e30d972016-03-15 13:20:19 -07001040 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1041 # patching on a device that's already on the target build will damage the
1042 # system. Because operations like move don't check the block state, they
1043 # always apply the changes unconditionally.
1044 if blockimgdiff_version <= 2:
1045 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001046 script.AssertSomeFingerprint(source_fp)
1047 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001048 script.AssertSomeThumbprint(
1049 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001050
1051 else: # blockimgdiff_version > 2
1052 if source_oem_props is None and target_oem_props is None:
1053 script.AssertSomeFingerprint(source_fp, target_fp)
1054 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -07001055 script.AssertSomeThumbprint(
1056 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1057 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001058 elif source_oem_props is None and target_oem_props is not None:
1059 script.AssertFingerprintOrThumbprint(
1060 source_fp,
1061 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1062 else:
1063 script.AssertFingerprintOrThumbprint(
1064 target_fp,
1065 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001066
Tao Baod8d14be2016-02-04 14:26:02 -08001067 # Check the required cache size (i.e. stashed blocks).
1068 size = []
1069 if system_diff:
1070 size.append(system_diff.required_cache)
1071 if vendor_diff:
1072 size.append(vendor_diff.required_cache)
1073
Geremy Condra36bd3652014-02-06 19:45:10 -08001074 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001075 boot_type, boot_device = common.GetTypeAndDevice(
1076 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001077 d = common.Difference(target_boot, source_boot)
1078 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001079 if d is None:
1080 include_full_boot = True
1081 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1082 else:
1083 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001084
Tao Bao89fbb0f2017-01-10 10:47:58 -08001085 print("boot target: %d source: %d diff: %d" % (
1086 target_boot.size, source_boot.size, len(d)))
Geremy Condra36bd3652014-02-06 19:45:10 -08001087
Doug Zongkerf8340082014-08-05 10:39:37 -07001088 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001089
Doug Zongkerf8340082014-08-05 10:39:37 -07001090 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1091 (boot_type, boot_device,
1092 source_boot.size, source_boot.sha1,
1093 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -08001094 size.append(target_boot.size)
1095
1096 if size:
1097 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001098
1099 device_specific.IncrementalOTA_VerifyEnd()
1100
1101 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001102 # Stage 1/3: (b) Write recovery image to /boot.
1103 _WriteRecoveryImageToBoot(script, output_zip)
1104
Geremy Condra36bd3652014-02-06 19:45:10 -08001105 script.AppendExtra("""
1106set_stage("%(bcb_dev)s", "2/3");
1107reboot_now("%(bcb_dev)s", "");
1108else
1109""" % bcb_dev)
1110
Tao Baod42e97e2016-11-30 12:11:57 -08001111 # Stage 3/3: Make changes.
1112 script.Comment("Stage 3/3")
1113
Jesse Zhao75bcea02015-01-06 10:59:53 -08001114 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001115 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001116 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001117 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001118
Geremy Condra36bd3652014-02-06 19:45:10 -08001119 script.Comment("---- start making changes here ----")
1120
1121 device_specific.IncrementalOTA_InstallBegin()
1122
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001123 system_diff.WriteScript(script, output_zip,
1124 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001125
Doug Zongkerfc44a512014-08-26 13:10:25 -07001126 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001127 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001128
1129 if OPTIONS.two_step:
1130 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1131 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001132 print("writing full boot image (forced by two-step mode)")
Geremy Condra36bd3652014-02-06 19:45:10 -08001133
1134 if not OPTIONS.two_step:
1135 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001136 if include_full_boot:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001137 print("boot image changed; including full.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001138 script.Print("Installing boot image...")
1139 script.WriteRawImage("/boot", "boot.img")
1140 else:
1141 # Produce the boot image by applying a patch to the current
1142 # contents of the boot partition, and write it back to the
1143 # partition.
Tao Bao89fbb0f2017-01-10 10:47:58 -08001144 print("boot image changed; including patch.")
Doug Zongkerf8340082014-08-05 10:39:37 -07001145 script.Print("Patching boot image...")
1146 script.ShowProgress(0.1, 10)
1147 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1148 % (boot_type, boot_device,
1149 source_boot.size, source_boot.sha1,
1150 target_boot.size, target_boot.sha1),
1151 "-",
1152 target_boot.size, target_boot.sha1,
1153 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001154 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001155 print("boot image unchanged; skipping.")
Geremy Condra36bd3652014-02-06 19:45:10 -08001156
1157 # Do device-specific installation (eg, write radio image).
1158 device_specific.IncrementalOTA_InstallEnd()
1159
1160 if OPTIONS.extra_script is not None:
1161 script.AppendExtra(OPTIONS.extra_script)
1162
Doug Zongker922206e2014-03-04 13:16:24 -08001163 if OPTIONS.wipe_user_data:
1164 script.Print("Erasing user data...")
1165 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001166 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001167
Geremy Condra36bd3652014-02-06 19:45:10 -08001168 if OPTIONS.two_step:
1169 script.AppendExtra("""
1170set_stage("%(bcb_dev)s", "");
1171endif;
1172endif;
1173""" % bcb_dev)
1174
1175 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001176 # For downgrade OTAs, we prefer to use the update-binary in the source
1177 # build that is actually newer than the one in the target build.
1178 if OPTIONS.downgrade:
1179 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1180 else:
1181 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001182 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001183 WriteMetadata(metadata, output_zip)
1184
Doug Zongker32b527d2014-03-04 10:03:02 -08001185
Tao Bao9bc6bb22015-11-09 16:58:28 -08001186def WriteVerifyPackage(input_zip, output_zip):
1187 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1188
1189 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1190 recovery_mount_options = OPTIONS.info_dict.get(
1191 "recovery_mount_options")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001192 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001193 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -08001194 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001195
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001196 target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
1197 OPTIONS.info_dict)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001198 metadata = {
1199 "post-build": target_fp,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001200 "pre-device": GetOemProperty("ro.product.device", oem_props,
1201 oem_dicts and oem_dicts[0],
Tao Bao9bc6bb22015-11-09 16:58:28 -08001202 OPTIONS.info_dict),
1203 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1204 }
1205
1206 device_specific = common.DeviceSpecificParams(
1207 input_zip=input_zip,
1208 input_version=OPTIONS.info_dict["recovery_api_version"],
1209 output_zip=output_zip,
1210 script=script,
1211 input_tmp=OPTIONS.input_tmp,
1212 metadata=metadata,
1213 info_dict=OPTIONS.info_dict)
1214
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001215 AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001216
1217 script.Print("Verifying device images against %s..." % target_fp)
1218 script.AppendExtra("")
1219
1220 script.Print("Verifying boot...")
1221 boot_img = common.GetBootableImage(
1222 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1223 boot_type, boot_device = common.GetTypeAndDevice(
1224 "/boot", OPTIONS.info_dict)
1225 script.Verify("%s:%s:%d:%s" % (
1226 boot_type, boot_device, boot_img.size, boot_img.sha1))
1227 script.AppendExtra("")
1228
1229 script.Print("Verifying recovery...")
1230 recovery_img = common.GetBootableImage(
1231 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1232 recovery_type, recovery_device = common.GetTypeAndDevice(
1233 "/recovery", OPTIONS.info_dict)
1234 script.Verify("%s:%s:%d:%s" % (
1235 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1236 script.AppendExtra("")
1237
1238 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1239 system_tgt.ResetFileMap()
1240 system_diff = common.BlockDifference("system", system_tgt, src=None)
1241 system_diff.WriteStrictVerifyScript(script)
1242
1243 if HasVendorPartition(input_zip):
1244 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1245 vendor_tgt.ResetFileMap()
1246 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1247 vendor_diff.WriteStrictVerifyScript(script)
1248
1249 # Device specific partitions, such as radio, bootloader and etc.
1250 device_specific.VerifyOTA_Assertions()
1251
1252 script.SetProgress(1.0)
1253 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001254 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001255 WriteMetadata(metadata, output_zip)
1256
1257
Tao Baoc098e9e2016-01-07 13:03:56 -08001258def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1259 source_file=None):
1260 """Generate an Android OTA package that has A/B update payload."""
1261
Tao Bao2dd1c482017-02-03 16:49:39 -08001262 def ComputeStreamingMetadata(zip_file, reserve_space=False,
1263 expected_length=None):
1264 """Compute the streaming metadata for a given zip.
1265
1266 When 'reserve_space' is True, we reserve extra space for the offset and
1267 length of the metadata entry itself, although we don't know the final
1268 values until the package gets signed. This function will be called again
1269 after signing. We then write the actual values and pad the string to the
1270 length we set earlier. Note that we can't use the actual length of the
1271 metadata entry in the second run. Otherwise the offsets for other entries
1272 will be changing again.
1273 """
Tao Baoc96316c2017-01-24 22:10:49 -08001274
1275 def ComputeEntryOffsetSize(name):
1276 """Compute the zip entry offset and size."""
1277 info = zip_file.getinfo(name)
1278 offset = info.header_offset + len(info.FileHeader())
1279 size = info.file_size
Tao Bao2dd1c482017-02-03 16:49:39 -08001280 return '%s:%d:%d' % (os.path.basename(name), offset, size)
Tao Baoc96316c2017-01-24 22:10:49 -08001281
1282 # payload.bin and payload_properties.txt must exist.
1283 offsets = [ComputeEntryOffsetSize('payload.bin'),
1284 ComputeEntryOffsetSize('payload_properties.txt')]
1285
1286 # care_map.txt is available only if dm-verity is enabled.
1287 if 'care_map.txt' in zip_file.namelist():
1288 offsets.append(ComputeEntryOffsetSize('care_map.txt'))
Tao Bao2dd1c482017-02-03 16:49:39 -08001289
1290 # 'META-INF/com/android/metadata' is required. We don't know its actual
1291 # offset and length (as well as the values for other entries). So we
1292 # reserve 10-byte as a placeholder, which is to cover the space for metadata
1293 # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
1294 # beginning of the zip), as well as the possible value changes in other
1295 # entries.
1296 if reserve_space:
1297 offsets.append('metadata:' + ' ' * 10)
1298 else:
1299 offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
1300
1301 value = ','.join(offsets)
1302 if expected_length is not None:
1303 assert len(value) <= expected_length, \
1304 'Insufficient reserved space: reserved=%d, actual=%d' % (
1305 expected_length, len(value))
1306 value += ' ' * (expected_length - len(value))
1307 return value
Tao Baoc96316c2017-01-24 22:10:49 -08001308
Alex Deymod8d96ec2016-06-10 16:38:31 -07001309 # The place where the output from the subprocess should go.
1310 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1311
Tao Baoc098e9e2016-01-07 13:03:56 -08001312 # Setup signing keys.
1313 if OPTIONS.package_key is None:
1314 OPTIONS.package_key = OPTIONS.info_dict.get(
1315 "default_system_dev_certificate",
1316 "build/target/product/security/testkey")
1317
Tao Baodea0f8b2016-06-20 17:55:06 -07001318 # A/B updater expects a signing key in RSA format. Gets the key ready for
1319 # later use in step 3, unless a payload_signer has been specified.
1320 if OPTIONS.payload_signer is None:
1321 cmd = ["openssl", "pkcs8",
1322 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1323 "-inform", "DER", "-nocrypt"]
1324 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1325 cmd.extend(["-out", rsa_key])
Tao Bao6047c242016-06-21 13:35:26 -07001326 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1327 p1.communicate()
Tao Baodea0f8b2016-06-20 17:55:06 -07001328 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001329
Tao Baodea0f8b2016-06-20 17:55:06 -07001330 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001331 temp_zip_file = tempfile.NamedTemporaryFile()
1332 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1333 compression=zipfile.ZIP_DEFLATED)
1334
1335 # Metadata to comply with Android OTA package format.
1336 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001337 oem_dicts = None
Tao Baoc098e9e2016-01-07 13:03:56 -08001338 if oem_props:
Tao Baoebce6972017-03-06 10:22:20 -08001339 oem_dicts = _LoadOemDicts(None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001340
1341 metadata = {
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001342 "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001343 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001344 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1345 OPTIONS.info_dict),
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001346 "pre-device": GetOemProperty("ro.product.device", oem_props,
1347 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001348 OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001349 "ota-required-cache": "0",
1350 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001351 }
1352
1353 if source_file is not None:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001354 metadata["pre-build"] = CalculateFingerprint(oem_props,
1355 oem_dicts and oem_dicts[0],
Tao Baoc098e9e2016-01-07 13:03:56 -08001356 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001357 metadata["pre-build-incremental"] = GetBuildProp(
1358 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001359
Tao Baob31892e2017-02-07 11:21:17 -08001360 HandleDowngradeMetadata(metadata)
1361 else:
1362 metadata["post-timestamp"] = GetBuildProp(
1363 "ro.build.date.utc", OPTIONS.info_dict)
1364
Tao Baoc098e9e2016-01-07 13:03:56 -08001365 # 1. Generate payload.
1366 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1367 cmd = ["brillo_update_payload", "generate",
1368 "--payload", payload_file,
1369 "--target_image", target_file]
1370 if source_file is not None:
1371 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001372 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1373 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001374 assert p1.returncode == 0, "brillo_update_payload generate failed"
1375
1376 # 2. Generate hashes of the payload and metadata files.
1377 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1378 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1379 cmd = ["brillo_update_payload", "hash",
1380 "--unsigned_payload", payload_file,
1381 "--signature_size", "256",
1382 "--metadata_hash_file", metadata_sig_file,
1383 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001384 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1385 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001386 assert p1.returncode == 0, "brillo_update_payload hash failed"
1387
1388 # 3. Sign the hashes and insert them back into the payload file.
1389 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1390 suffix=".bin")
1391 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1392 suffix=".bin")
1393 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001394 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001395 cmd = [OPTIONS.payload_signer]
1396 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001397 else:
1398 cmd = ["openssl", "pkeyutl", "-sign",
1399 "-inkey", rsa_key,
1400 "-pkeyopt", "digest:sha256"]
1401 cmd.extend(["-in", payload_sig_file,
1402 "-out", signed_payload_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001403 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1404 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001405 assert p1.returncode == 0, "openssl sign payload failed"
1406
1407 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001408 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001409 cmd = [OPTIONS.payload_signer]
1410 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001411 else:
1412 cmd = ["openssl", "pkeyutl", "-sign",
1413 "-inkey", rsa_key,
1414 "-pkeyopt", "digest:sha256"]
1415 cmd.extend(["-in", metadata_sig_file,
1416 "-out", signed_metadata_sig_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001417 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1418 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001419 assert p1.returncode == 0, "openssl sign metadata failed"
1420
1421 # 3c. Insert the signatures back into the payload file.
1422 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1423 suffix=".bin")
1424 cmd = ["brillo_update_payload", "sign",
1425 "--unsigned_payload", payload_file,
1426 "--payload", signed_payload_file,
1427 "--signature_size", "256",
1428 "--metadata_signature_file", signed_metadata_sig_file,
1429 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001430 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1431 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001432 assert p1.returncode == 0, "brillo_update_payload sign failed"
1433
Alex Deymo19241c12016-02-04 22:29:29 -08001434 # 4. Dump the signed payload properties.
1435 properties_file = common.MakeTempFile(prefix="payload-properties-",
1436 suffix=".txt")
1437 cmd = ["brillo_update_payload", "properties",
1438 "--payload", signed_payload_file,
1439 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001440 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1441 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001442 assert p1.returncode == 0, "brillo_update_payload properties failed"
1443
Tao Bao7c5dc572016-06-14 17:48:11 -07001444 if OPTIONS.wipe_user_data:
1445 with open(properties_file, "a") as f:
1446 f.write("POWERWASH=1\n")
1447 metadata["ota-wipe"] = "yes"
1448
Tao Baoc96316c2017-01-24 22:10:49 -08001449 # Add the signed payload file and properties into the zip. In order to
1450 # support streaming, we pack payload.bin, payload_properties.txt and
1451 # care_map.txt as ZIP_STORED. So these entries can be read directly with
1452 # the offset and length pairs.
Tao Baoc098e9e2016-01-07 13:03:56 -08001453 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1454 compress_type=zipfile.ZIP_STORED)
Tao Baoc96316c2017-01-24 22:10:49 -08001455 common.ZipWrite(output_zip, properties_file,
1456 arcname="payload_properties.txt",
1457 compress_type=zipfile.ZIP_STORED)
Tao Baoc098e9e2016-01-07 13:03:56 -08001458
Tianjie Xucfa86222016-03-07 16:31:19 -08001459 # If dm-verity is supported for the device, copy contents of care_map
1460 # into A/B OTA package.
1461 if OPTIONS.info_dict.get("verity") == "true":
1462 target_zip = zipfile.ZipFile(target_file, "r")
1463 care_map_path = "META/care_map.txt"
1464 namelist = target_zip.namelist()
1465 if care_map_path in namelist:
1466 care_map_data = target_zip.read(care_map_path)
Tao Baoc96316c2017-01-24 22:10:49 -08001467 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
1468 compress_type=zipfile.ZIP_STORED)
Tianjie Xucfa86222016-03-07 16:31:19 -08001469 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001470 print("Warning: cannot find care map file in target_file package")
Tianjie Xucfa86222016-03-07 16:31:19 -08001471 common.ZipClose(target_zip)
1472
Tao Bao2dd1c482017-02-03 16:49:39 -08001473 # Write the current metadata entry with placeholders.
Tao Baobfdcb122017-01-31 15:06:05 -08001474 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
Tao Bao2dd1c482017-02-03 16:49:39 -08001475 output_zip, reserve_space=True)
Tao Baoc96316c2017-01-24 22:10:49 -08001476 WriteMetadata(metadata, output_zip)
1477 common.ZipClose(output_zip)
1478
Tao Bao2dd1c482017-02-03 16:49:39 -08001479 # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
1480 # zip entries, as well as padding the entry headers. We do a preliminary
1481 # signing (with an incomplete metadata entry) to allow that to happen. Then
1482 # compute the zip entry offsets, write back the final metadata and do the
1483 # final signing.
1484 prelim_signing = tempfile.NamedTemporaryFile()
1485 SignOutput(temp_zip_file.name, prelim_signing.name)
1486 common.ZipClose(temp_zip_file)
Tao Baoc96316c2017-01-24 22:10:49 -08001487
Tao Bao2dd1c482017-02-03 16:49:39 -08001488 # Open the signed zip. Compute the final metadata that's needed for streaming.
1489 prelim_zip = zipfile.ZipFile(prelim_signing, "r",
1490 compression=zipfile.ZIP_DEFLATED)
1491 expected_length = len(metadata['ota-streaming-property-files'])
1492 metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
1493 prelim_zip, reserve_space=False, expected_length=expected_length)
1494
1495 # Copy the zip entries, as we cannot update / delete entries with zipfile.
1496 final_signing = tempfile.NamedTemporaryFile()
1497 output_zip = zipfile.ZipFile(final_signing, "w",
1498 compression=zipfile.ZIP_DEFLATED)
1499 for item in prelim_zip.infolist():
1500 if item.filename == METADATA_NAME:
1501 continue
1502
1503 data = prelim_zip.read(item.filename)
1504 out_info = copy.copy(item)
1505 common.ZipWriteStr(output_zip, out_info, data)
1506
1507 # Now write the final metadata entry.
1508 WriteMetadata(metadata, output_zip)
1509 common.ZipClose(prelim_zip)
1510 common.ZipClose(output_zip)
1511
1512 # Re-sign the package after updating the metadata entry.
1513 SignOutput(final_signing.name, output_file)
1514 final_signing.close()
1515
1516 # Reopen the final signed zip to double check the streaming metadata.
Tao Baoc96316c2017-01-24 22:10:49 -08001517 output_zip = zipfile.ZipFile(output_file, "r")
Tao Bao2dd1c482017-02-03 16:49:39 -08001518 actual = metadata['ota-streaming-property-files'].strip()
1519 expected = ComputeStreamingMetadata(output_zip)
1520 assert actual == expected, \
1521 "Mismatching streaming metadata: %s vs %s." % (actual, expected)
Tao Baoc96316c2017-01-24 22:10:49 -08001522 common.ZipClose(output_zip)
1523
Tao Baoc098e9e2016-01-07 13:03:56 -08001524
Dan Albert8b72aef2015-03-23 19:13:21 -07001525class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001526 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001527 self.deferred_patch_list = None
Tao Bao89fbb0f2017-01-10 10:47:58 -08001528 print("Loading target...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001529 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001530 print("Loading source...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001531 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1532
1533 self.verbatim_targets = verbatim_targets = []
1534 self.patch_list = patch_list = []
1535 diffs = []
1536 self.renames = renames = {}
1537 known_paths = set()
1538 largest_source_size = 0
1539
1540 matching_file_cache = {}
1541 for fn, sf in source_data.items():
1542 assert fn == sf.name
1543 matching_file_cache["path:" + fn] = sf
1544 if fn in target_data.keys():
1545 AddToKnownPaths(fn, known_paths)
1546 # Only allow eligibility for filename/sha matching
1547 # if there isn't a perfect path match.
1548 if target_data.get(sf.name) is None:
1549 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1550 matching_file_cache["sha:" + sf.sha1] = sf
1551
1552 for fn in sorted(target_data.keys()):
1553 tf = target_data[fn]
1554 assert fn == tf.name
1555 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1556 if sf is not None and sf.name != tf.name:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001557 print("File has moved from " + sf.name + " to " + tf.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001558 renames[sf.name] = tf
1559
1560 if sf is None or fn in OPTIONS.require_verbatim:
1561 # This file should be included verbatim
1562 if fn in OPTIONS.prohibit_verbatim:
1563 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Tao Bao89fbb0f2017-01-10 10:47:58 -08001564 print("send", fn, "verbatim")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001565 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001566 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001567 if fn in target_data.keys():
1568 AddToKnownPaths(fn, known_paths)
1569 elif tf.sha1 != sf.sha1:
1570 # File is different; consider sending as a patch
1571 diffs.append(common.Difference(tf, sf))
1572 else:
1573 # Target file data identical to source (may still be renamed)
1574 pass
1575
1576 common.ComputeDifferences(diffs)
1577
1578 for diff in diffs:
1579 tf, sf, d = diff.GetPatch()
1580 path = "/".join(tf.name.split("/")[:-1])
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001581 if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001582 path not in known_paths:
1583 # patch is almost as big as the file; don't bother patching
1584 # or a patch + rename cannot take place due to the target
1585 # directory not existing
1586 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001587 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001588 if sf.name in renames:
1589 del renames[sf.name]
1590 AddToKnownPaths(tf.name, known_paths)
1591 else:
1592 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1593 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1594 largest_source_size = max(largest_source_size, sf.size)
1595
1596 self.largest_source_size = largest_source_size
1597
1598 def EmitVerification(self, script):
1599 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001600 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001601 if tf.name != sf.name:
1602 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1603 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1604 so_far += sf.size
1605 return so_far
1606
Michael Runge63f01de2014-10-28 19:24:19 -07001607 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001608 for fn, _, sha1 in self.verbatim_targets:
1609 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001610 script.FileCheck("/"+fn, sha1)
1611 for tf, _, _, _ in self.patch_list:
1612 script.FileCheck(tf.name, tf.sha1)
1613
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001614 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001615 file_list = ["/" + i[0] for i in self.verbatim_targets]
1616 file_list += ["/" + i for i in self.source_data
1617 if i not in self.target_data and i not in self.renames]
1618 file_list += list(extras)
1619 # Sort the list in descending order, which removes all the files first
1620 # before attempting to remove the folder. (Bug: 22960996)
1621 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001622
1623 def TotalPatchSize(self):
1624 return sum(i[1].size for i in self.patch_list)
1625
1626 def EmitPatches(self, script, total_patch_size, so_far):
1627 self.deferred_patch_list = deferred_patch_list = []
1628 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001629 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001630 if tf.name == "system/build.prop":
1631 deferred_patch_list.append(item)
1632 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001633 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001634 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001635 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1636 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001637 so_far += tf.size
1638 script.SetProgress(so_far / total_patch_size)
1639 return so_far
1640
1641 def EmitDeferredPatches(self, script):
1642 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001643 tf, sf, _, _ = item
1644 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1645 "patch/" + sf.name + ".p")
1646 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001647
1648 def EmitRenames(self, script):
1649 if len(self.renames) > 0:
1650 script.Print("Renaming files...")
1651 for src, tgt in self.renames.iteritems():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001652 print("Renaming " + src + " to " + tgt.name)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001653 script.RenameFile(src, tgt.name)
1654
1655
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001656def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001657 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1658 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1659
Doug Zongker26e66192014-02-20 13:22:07 -08001660 if (OPTIONS.block_based and
1661 target_has_recovery_patch and
1662 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001663 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1664
Doug Zongker37974732010-09-16 17:44:38 -07001665 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1666 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001667
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001668 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001669 print("WARNING: generating edify script for a source that "
1670 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001671 script = edify_generator.EdifyGenerator(
1672 source_version, OPTIONS.target_info_dict,
1673 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001674
Tao Bao34b47bf2015-06-22 19:17:41 -07001675 recovery_mount_options = OPTIONS.source_info_dict.get(
1676 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001677 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1678 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001679 oem_dicts = None
Tao Bao3e30d972016-03-15 13:20:19 -07001680 if source_oem_props or target_oem_props:
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001681 oem_dicts = _LoadOemDicts(script, recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001682
Dan Albert8b72aef2015-03-23 19:13:21 -07001683 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001684 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001685 oem_dicts and oem_dicts[0],
1686 OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001687 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001688 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001689
Tao Baob31892e2017-02-07 11:21:17 -08001690 HandleDowngradeMetadata(metadata)
Tao Bao5d182562016-02-23 11:38:39 -08001691
Doug Zongker05d3dea2009-06-22 11:32:31 -07001692 device_specific = common.DeviceSpecificParams(
1693 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001694 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001695 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001696 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001697 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001698 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001699 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001700 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001701
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001702 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001703 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001704 if HasVendorPartition(target_zip):
1705 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001706 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001707 else:
1708 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001709
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001710 target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001711 OPTIONS.target_info_dict)
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001712 source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
Dan Albert8b72aef2015-03-23 19:13:21 -07001713 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001714
Tao Bao3e30d972016-03-15 13:20:19 -07001715 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001716 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001717 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001718 script.AssertSomeThumbprint(
1719 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1720 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001721 elif source_oem_props is None and target_oem_props is not None:
1722 script.AssertFingerprintOrThumbprint(
1723 source_fp,
1724 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1725 else:
1726 script.AssertFingerprintOrThumbprint(
1727 target_fp,
1728 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001729
Doug Zongker2ea21062010-04-28 16:05:21 -07001730 metadata["pre-build"] = source_fp
1731 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001732 metadata["pre-build-incremental"] = GetBuildProp(
1733 "ro.build.version.incremental", OPTIONS.source_info_dict)
1734 metadata["post-build-incremental"] = GetBuildProp(
1735 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001736
Doug Zongker55d93282011-01-25 17:03:34 -08001737 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001738 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1739 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001740 target_boot = common.GetBootableImage(
1741 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001742 updating_boot = (not OPTIONS.two_step and
1743 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001744
Doug Zongker55d93282011-01-25 17:03:34 -08001745 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001746 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1747 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001748 target_recovery = common.GetBootableImage(
1749 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001750 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001751
Doug Zongker881dd402009-09-20 14:03:55 -07001752 # Here's how we divide up the progress bar:
1753 # 0.1 for verifying the start state (PatchCheck calls)
1754 # 0.8 for applying patches (ApplyPatch calls)
1755 # 0.1 for unpacking verbatim files, symlinking, and doing the
1756 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001757
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08001758 AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001759 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001760
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001761 # Two-step incremental package strategy (in chronological order,
1762 # which is *not* the order in which the generated script has
1763 # things):
1764 #
1765 # if stage is not "2/3" or "3/3":
1766 # do verification on current system
1767 # write recovery image to boot partition
1768 # set stage to "2/3"
1769 # reboot to boot partition and restart recovery
1770 # else if stage is "2/3":
1771 # write recovery image to recovery partition
1772 # set stage to "3/3"
1773 # reboot to recovery partition and restart recovery
1774 # else:
1775 # (stage must be "3/3")
1776 # perform update:
1777 # patch system files, etc.
1778 # force full install of new boot image
1779 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001780 # complete script normally
1781 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001782
1783 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001784 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001785 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001786 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001787 assert fs.fs_type.upper() == "EMMC", \
1788 "two-step packages only supported on devices with EMMC /misc partitions"
1789 bcb_dev = {"bcb_dev": fs.device}
1790 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1791 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001792if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001793""" % bcb_dev)
Tao Baod42e97e2016-11-30 12:11:57 -08001794
1795 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1796 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001797 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001798 script.WriteRawImage("/recovery", "recovery.img")
1799 script.AppendExtra("""
1800set_stage("%(bcb_dev)s", "3/3");
1801reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001802else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001803""" % bcb_dev)
1804
Tao Baod42e97e2016-11-30 12:11:57 -08001805 # Stage 1/3: (a) Verify the current system.
1806 script.Comment("Stage 1/3")
1807
Tao Bao6c55a8a2015-04-08 15:30:27 -07001808 # Dump fingerprints
1809 script.Print("Source: %s" % (source_fp,))
1810 script.Print("Target: %s" % (target_fp,))
1811
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001812 script.Print("Verifying current system...")
1813
Doug Zongkere5ff5902012-01-17 10:55:37 -08001814 device_specific.IncrementalOTA_VerifyBegin()
1815
Doug Zongker881dd402009-09-20 14:03:55 -07001816 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001817 so_far = system_diff.EmitVerification(script)
1818 if vendor_diff:
1819 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001820
Tao Baod8d14be2016-02-04 14:26:02 -08001821 size = []
1822 if system_diff.patch_list:
1823 size.append(system_diff.largest_source_size)
1824 if vendor_diff:
1825 if vendor_diff.patch_list:
1826 size.append(vendor_diff.largest_source_size)
1827
Doug Zongker5da317e2009-06-02 13:38:17 -07001828 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001829 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001830 _, _, d = d.ComputePatch()
Tao Bao89fbb0f2017-01-10 10:47:58 -08001831 print("boot target: %d source: %d diff: %d" % (
1832 target_boot.size, source_boot.size, len(d)))
Doug Zongker5da317e2009-06-02 13:38:17 -07001833
Doug Zongker048e7ca2009-06-15 14:31:53 -07001834 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001835
Tao Baodd24da92015-07-29 14:09:23 -07001836 boot_type, boot_device = common.GetTypeAndDevice(
1837 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001838
1839 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1840 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001841 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001842 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001843 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001844 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001845
Tao Baod8d14be2016-02-04 14:26:02 -08001846 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001847 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001848
Doug Zongker05d3dea2009-06-22 11:32:31 -07001849 device_specific.IncrementalOTA_VerifyEnd()
1850
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001851 if OPTIONS.two_step:
Tao Baod42e97e2016-11-30 12:11:57 -08001852 # Stage 1/3: (b) Write recovery image to /boot.
1853 _WriteRecoveryImageToBoot(script, output_zip)
1854
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001855 script.AppendExtra("""
1856set_stage("%(bcb_dev)s", "2/3");
1857reboot_now("%(bcb_dev)s", "");
1858else
1859""" % bcb_dev)
1860
Tao Baod42e97e2016-11-30 12:11:57 -08001861 # Stage 3/3: Make changes.
1862 script.Comment("Stage 3/3")
1863
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001864 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001865
Doug Zongkere5ff5902012-01-17 10:55:37 -08001866 device_specific.IncrementalOTA_InstallBegin()
1867
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001868 if OPTIONS.two_step:
1869 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1870 script.WriteRawImage("/boot", "boot.img")
Tao Bao89fbb0f2017-01-10 10:47:58 -08001871 print("writing full boot image (forced by two-step mode)")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001872
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001873 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001874 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1875 if vendor_diff:
1876 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001877
Doug Zongker881dd402009-09-20 14:03:55 -07001878 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001879 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1880 if vendor_diff:
1881 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001882 if updating_boot:
1883 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001884
1885 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001886 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1887 if vendor_diff:
1888 script.Print("Patching vendor files...")
1889 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001890
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001891 if not OPTIONS.two_step:
1892 if updating_boot:
1893 # Produce the boot image by applying a patch to the current
1894 # contents of the boot partition, and write it back to the
1895 # partition.
1896 script.Print("Patching boot image...")
1897 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1898 % (boot_type, boot_device,
1899 source_boot.size, source_boot.sha1,
1900 target_boot.size, target_boot.sha1),
1901 "-",
1902 target_boot.size, target_boot.sha1,
1903 source_boot.sha1, "patch/boot.img.p")
1904 so_far += target_boot.size
1905 script.SetProgress(so_far / total_patch_size)
Tao Bao89fbb0f2017-01-10 10:47:58 -08001906 print("boot image changed; including.")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001907 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001908 print("boot image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001909
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001910 system_items = ItemSet("system", "META/filesystem_config.txt")
1911 if vendor_diff:
1912 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1913
Doug Zongkereef39442009-04-02 12:14:19 -07001914 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001915 # Recovery is generated as a patch using both the boot image
1916 # (which contains the same linux kernel as recovery) and the file
1917 # /system/etc/recovery-resource.dat (which contains all the images
1918 # used in the recovery UI) as sources. This lets us minimize the
1919 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001920 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001921 # For older builds where recovery-resource.dat is not present, we
1922 # use only the boot image as the source.
1923
Doug Zongkerc9253822014-02-04 12:17:58 -08001924 if not target_has_recovery_patch:
1925 def output_sink(fn, data):
1926 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001927 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001928
1929 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1930 target_recovery, target_boot)
1931 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001932 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001933 "/system/etc/install-recovery.sh"])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001934 print("recovery image changed; including as patch from boot.")
Doug Zongkereef39442009-04-02 12:14:19 -07001935 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001936 print("recovery image unchanged; skipping.")
Doug Zongkereef39442009-04-02 12:14:19 -07001937
Doug Zongker881dd402009-09-20 14:03:55 -07001938 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001939
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001940 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1941 if vendor_diff:
1942 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1943
1944 temp_script = script.MakeTemporary()
1945 system_items.GetMetadata(target_zip)
1946 system_items.Get("system").SetPermissions(temp_script)
1947 if vendor_diff:
1948 vendor_items.GetMetadata(target_zip)
1949 vendor_items.Get("vendor").SetPermissions(temp_script)
1950
1951 # Note that this call will mess up the trees of Items, so make sure
1952 # we're done with them.
1953 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1954 if vendor_diff:
1955 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001956
1957 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001958 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1959
1960 # Delete all the symlinks in source that aren't in target. This
1961 # needs to happen before verbatim files are unpacked, in case a
1962 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001963
1964 # If a symlink in the source will be replaced by a regular file, we cannot
1965 # delete the symlink/file in case the package gets applied again. For such
1966 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1967 # (Bug: 23646151)
1968 replaced_symlinks = dict()
1969 if system_diff:
1970 for i in system_diff.verbatim_targets:
1971 replaced_symlinks["/%s" % (i[0],)] = i[2]
1972 if vendor_diff:
1973 for i in vendor_diff.verbatim_targets:
1974 replaced_symlinks["/%s" % (i[0],)] = i[2]
1975
1976 if system_diff:
1977 for tf in system_diff.renames.values():
1978 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1979 if vendor_diff:
1980 for tf in vendor_diff.renames.values():
1981 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1982
1983 always_delete = []
1984 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001985 for dest, link in source_symlinks:
1986 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001987 if link in replaced_symlinks:
1988 may_delete.append((link, replaced_symlinks[link]))
1989 else:
1990 always_delete.append(link)
1991 script.DeleteFiles(always_delete)
1992 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001993
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001994 if system_diff.verbatim_targets:
1995 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001996 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001997 if vendor_diff and vendor_diff.verbatim_targets:
1998 script.Print("Unpacking new vendor files...")
1999 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002000
Doug Zongkerc9253822014-02-04 12:17:58 -08002001 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08002002 script.Print("Unpacking new recovery...")
2003 script.UnpackPackageDir("recovery", "/system")
2004
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002005 system_diff.EmitRenames(script)
2006 if vendor_diff:
2007 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08002008
Doug Zongker05d3dea2009-06-22 11:32:31 -07002009 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07002010
2011 # Create all the symlinks that don't already exist, or point to
2012 # somewhere different than what we want. Delete each symlink before
2013 # creating it, since the 'symlink' command won't overwrite.
2014 to_create = []
2015 for dest, link in target_symlinks:
2016 if link in source_symlinks_d:
2017 if dest != source_symlinks_d[link]:
2018 to_create.append((dest, link))
2019 else:
2020 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002021 script.DeleteFiles([i[1] for i in to_create])
2022 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07002023
2024 # Now that the symlinks are created, we can set all the
2025 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07002026 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07002027
Doug Zongker881dd402009-09-20 14:03:55 -07002028 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07002029 device_specific.IncrementalOTA_InstallEnd()
2030
Doug Zongker1c390a22009-05-14 19:06:36 -07002031 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07002032 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07002033
Doug Zongkere92f15a2011-08-26 13:46:40 -07002034 # Patch the build.prop file last, so if something fails but the
2035 # device can still come up, it appears to be the old build and will
2036 # get set the OTA package again to retry.
2037 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07002038 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07002039
Doug Zongker922206e2014-03-04 13:16:24 -08002040 if OPTIONS.wipe_user_data:
2041 script.Print("Erasing user data...")
2042 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08002043 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08002044
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002045 if OPTIONS.two_step:
2046 script.AppendExtra("""
2047set_stage("%(bcb_dev)s", "");
2048endif;
2049endif;
2050""" % bcb_dev)
2051
Michael Runge63f01de2014-10-28 19:24:19 -07002052 if OPTIONS.verify and system_diff:
2053 script.Print("Remounting and verifying system partition files...")
2054 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08002055 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002056 system_diff.EmitExplicitTargetVerification(script)
2057
2058 if OPTIONS.verify and vendor_diff:
2059 script.Print("Remounting and verifying vendor partition files...")
2060 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08002061 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07002062 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08002063
2064 # For downgrade OTAs, we prefer to use the update-binary in the source
2065 # build that is actually newer than the one in the target build.
2066 if OPTIONS.downgrade:
2067 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
2068 else:
2069 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07002070
Tao Baod8d14be2016-02-04 14:26:02 -08002071 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07002072 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07002073
2074
2075def main(argv):
2076
2077 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08002078 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002079 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07002080 elif o in ("-k", "--package_key"):
2081 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07002082 elif o in ("-i", "--incremental_from"):
2083 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07002084 elif o == "--full_radio":
2085 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07002086 elif o == "--full_bootloader":
2087 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002088 elif o in ("-w", "--wipe_user_data"):
2089 OPTIONS.wipe_user_data = True
Tao Bao5d182562016-02-23 11:38:39 -08002090 elif o == "--downgrade":
2091 OPTIONS.downgrade = True
2092 OPTIONS.wipe_user_data = True
Tao Bao3e6161a2017-02-28 11:48:48 -08002093 elif o == "--override_timestamp":
2094 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07002095 elif o in ("-o", "--oem_settings"):
Alain Vongsouvanh7f804ba2017-02-16 13:06:55 -08002096 OPTIONS.oem_source = a.split(',')
Tao Bao8608cde2016-02-25 19:49:55 -08002097 elif o == "--oem_no_mount":
2098 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07002099 elif o in ("-e", "--extra_script"):
2100 OPTIONS.extra_script = a
Martin Blumenstingl374e1142014-05-31 20:42:55 +02002101 elif o in ("-t", "--worker_threads"):
2102 if a.isdigit():
2103 OPTIONS.worker_threads = int(a)
2104 else:
2105 raise ValueError("Cannot parse value %r for option %r - only "
2106 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08002107 elif o in ("-2", "--two_step"):
2108 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002109 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002110 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002111 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002112 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002113 elif o == "--block":
2114 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002115 elif o in ("-b", "--binary"):
2116 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002117 elif o in ("--no_fallback_to_full",):
2118 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002119 elif o == "--stash_threshold":
2120 try:
2121 OPTIONS.stash_threshold = float(a)
2122 except ValueError:
2123 raise ValueError("Cannot parse value %r for option %r - expecting "
2124 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002125 elif o == "--gen_verify":
2126 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002127 elif o == "--log_diff":
2128 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002129 elif o == "--payload_signer":
2130 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002131 elif o == "--payload_signer_args":
2132 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002133 else:
2134 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002135 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002136
2137 args = common.ParseOptions(argv, __doc__,
Tao Bao2a0d1da2017-01-13 11:56:54 -08002138 extra_opts="b:k:i:d:we:t:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002139 extra_long_opts=[
2140 "board_config=",
2141 "package_key=",
2142 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002143 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002144 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002145 "wipe_user_data",
Tao Bao5d182562016-02-23 11:38:39 -08002146 "downgrade",
Tao Bao3e6161a2017-02-28 11:48:48 -08002147 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002148 "extra_script=",
2149 "worker_threads=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002150 "two_step",
2151 "no_signing",
2152 "block",
2153 "binary=",
2154 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08002155 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002156 "verify",
2157 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002158 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002159 "gen_verify",
2160 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002161 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002162 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002163 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002164
2165 if len(args) != 2:
2166 common.Usage(__doc__)
2167 sys.exit(1)
2168
Tao Bao5d182562016-02-23 11:38:39 -08002169 if OPTIONS.downgrade:
2170 # Sanity check to enforce a data wipe.
2171 if not OPTIONS.wipe_user_data:
2172 raise ValueError("Cannot downgrade without a data wipe")
2173
2174 # We should only allow downgrading incrementals (as opposed to full).
2175 # Otherwise the device may go back from arbitrary build with this full
2176 # OTA package.
2177 if OPTIONS.incremental_source is None:
Elliott Hughesd8a52f92016-06-20 14:35:47 -07002178 raise ValueError("Cannot generate downgradable full OTAs")
Tao Bao5d182562016-02-23 11:38:39 -08002179
Tao Bao3e6161a2017-02-28 11:48:48 -08002180 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
2181 "Cannot have --downgrade AND --override_timestamp both"
2182
Tao Baoc098e9e2016-01-07 13:03:56 -08002183 # Load the dict file from the zip directly to have a peek at the OTA type.
2184 # For packages using A/B update, unzipping is not needed.
2185 input_zip = zipfile.ZipFile(args[0], "r")
2186 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2187 common.ZipClose(input_zip)
2188
2189 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2190
2191 if ab_update:
2192 if OPTIONS.incremental_source is not None:
2193 OPTIONS.target_info_dict = OPTIONS.info_dict
2194 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2195 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2196 common.ZipClose(source_zip)
2197
2198 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002199 print("--- target info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002200 common.DumpInfoDict(OPTIONS.info_dict)
2201
2202 if OPTIONS.incremental_source is not None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002203 print("--- source info ---")
Tao Baoc098e9e2016-01-07 13:03:56 -08002204 common.DumpInfoDict(OPTIONS.source_info_dict)
2205
2206 WriteABOTAPackageWithBrilloScript(
2207 target_file=args[0],
2208 output_file=args[1],
2209 source_file=OPTIONS.incremental_source)
2210
Tao Bao89fbb0f2017-01-10 10:47:58 -08002211 print("done.")
Tao Baoc098e9e2016-01-07 13:03:56 -08002212 return
2213
Doug Zongker1c390a22009-05-14 19:06:36 -07002214 if OPTIONS.extra_script is not None:
2215 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2216
Tao Bao89fbb0f2017-01-10 10:47:58 -08002217 print("unzipping target target-files...")
Tao Bao6b0b2f92017-03-05 11:38:11 -08002218 OPTIONS.input_tmp, input_zip = common.UnzipTemp(
2219 args[0], UNZIP_PATTERN if OPTIONS.block_based else None)
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002220
Doug Zongkereef39442009-04-02 12:14:19 -07002221 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002222 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002223
Doug Zongker37974732010-09-16 17:44:38 -07002224 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002225 print("--- target info ---")
Doug Zongker37974732010-09-16 17:44:38 -07002226 common.DumpInfoDict(OPTIONS.info_dict)
2227
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002228 # If the caller explicitly specified the device-specific extensions
2229 # path via -s/--device_specific, use that. Otherwise, use
2230 # META/releasetools.py if it is present in the target target_files.
2231 # Otherwise, take the path of the file from 'tool_extensions' in the
2232 # info dict and look for that in the local filesystem, relative to
2233 # the current directory.
2234
Doug Zongker37974732010-09-16 17:44:38 -07002235 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002236 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2237 if os.path.exists(from_input):
Tao Bao89fbb0f2017-01-10 10:47:58 -08002238 print("(using device-specific extensions from target_files)")
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002239 OPTIONS.device_specific = from_input
2240 else:
2241 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2242
Doug Zongker37974732010-09-16 17:44:38 -07002243 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002244 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002245
Tao Baoc098e9e2016-01-07 13:03:56 -08002246 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002247 raise common.ExternalError(
2248 "--- target build has specified no recovery ---")
2249
Tao Bao767e3ac2015-11-10 12:19:19 -08002250 # Use the default key to sign the package if not specified with package_key.
2251 if not OPTIONS.no_signing:
2252 if OPTIONS.package_key is None:
2253 OPTIONS.package_key = OPTIONS.info_dict.get(
2254 "default_system_dev_certificate",
2255 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002256
Tao Bao767e3ac2015-11-10 12:19:19 -08002257 # Set up the output zip. Create a temporary zip file if signing is needed.
2258 if OPTIONS.no_signing:
2259 if os.path.exists(args[1]):
2260 os.unlink(args[1])
2261 output_zip = zipfile.ZipFile(args[1], "w",
2262 compression=zipfile.ZIP_DEFLATED)
2263 else:
2264 temp_zip_file = tempfile.NamedTemporaryFile()
2265 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2266 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002267
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002268 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002269 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002270 if cache_size is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002271 print("--- can't determine the cache partition size ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002272 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002273
Tao Bao9bc6bb22015-11-09 16:58:28 -08002274 # Generate a verify package.
2275 if OPTIONS.gen_verify:
2276 WriteVerifyPackage(input_zip, output_zip)
2277
Tao Bao767e3ac2015-11-10 12:19:19 -08002278 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002279 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002280 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002281
2282 # Generate an incremental OTA. It will fall back to generate a full OTA on
2283 # failure unless no_fallback_to_full is specified.
2284 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002285 print("unzipping source target-files...")
Tao Bao767e3ac2015-11-10 12:19:19 -08002286 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
Tao Bao6b0b2f92017-03-05 11:38:11 -08002287 OPTIONS.incremental_source,
2288 UNZIP_PATTERN if OPTIONS.block_based else None)
Tao Bao767e3ac2015-11-10 12:19:19 -08002289 OPTIONS.target_info_dict = OPTIONS.info_dict
2290 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2291 OPTIONS.source_tmp)
2292 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002293 print("--- source info ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002294 common.DumpInfoDict(OPTIONS.source_info_dict)
2295 try:
2296 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002297 if OPTIONS.log_diff:
2298 out_file = open(OPTIONS.log_diff, 'w')
2299 import target_files_diff
2300 target_files_diff.recursiveDiff('',
2301 OPTIONS.source_tmp,
2302 OPTIONS.input_tmp,
2303 out_file)
2304 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002305 except ValueError:
2306 if not OPTIONS.fallback_to_full:
2307 raise
Tao Bao89fbb0f2017-01-10 10:47:58 -08002308 print("--- failed to build incremental; falling back to full ---")
Tao Bao767e3ac2015-11-10 12:19:19 -08002309 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002310 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002311
Tao Bao767e3ac2015-11-10 12:19:19 -08002312 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002313
Tao Bao767e3ac2015-11-10 12:19:19 -08002314 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002315 if not OPTIONS.no_signing:
2316 SignOutput(temp_zip_file.name, args[1])
2317 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002318
Tao Bao89fbb0f2017-01-10 10:47:58 -08002319 print("done.")
Doug Zongkereef39442009-04-02 12:14:19 -07002320
2321
2322if __name__ == '__main__':
2323 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002324 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002325 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002326 except common.ExternalError as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08002327 print("\n ERROR: %s\n" % (e,))
Doug Zongkereef39442009-04-02 12:14:19 -07002328 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002329 finally:
2330 common.Cleanup()