blob: 24b42eefc28a76607f1d3b51d4e01b403972cdd8 [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
Michael Runge6e836112014-04-15 17:40:21 -070053 -o (--oem_settings) <file>
54 Use the file to specify the expected OEM-specific properties
55 on the OEM partition of the intended device.
56
Tao Baodf4cb0b2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Doug Zongkerdbfaae52009-04-21 17:12:54 -070063 -w (--wipe_user_data)
64 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Doug Zongker962069c2009-04-23 11:41:58 -070067 -n (--no_prereq)
68 Omit the timestamp prereq check normally included at the top of
69 the build scripts (used for developer OTA packages which
70 legitimately need to go back and forth).
71
Tao Bao4da324e2016-02-23 11:38:39 -080072 --downgrade
73 Intentionally generate an incremental OTA that updates from a newer
74 build to an older one (based on timestamp comparison). "post-timestamp"
75 will be replaced by "ota-downgrade=yes" in the metadata file. A data
76 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Baofa41fb22016-03-08 17:53:39 -080077 the metadata file. The update-binary in the source build will be used in
Tao Bao9f884e62017-02-28 11:48:48 -080078 the OTA package, unless --binary flag is specified. Please also check the
79 doc for --override_timestamp below.
80
81 --override_timestamp
82 Intentionally generate an incremental OTA that updates from a newer
83 build to an older one (based on timestamp comparison), by overriding the
84 timestamp in package metadata. This differs from --downgrade flag: we
85 know for sure this is NOT an actual downgrade case, but two builds are
86 cut in a reverse order. A legit use case is that we cut a new build C
87 (after having A and B), but want to enfore an update path of A -> C -> B.
88 Specifying --downgrade may not help since that would enforce a data wipe
89 for C -> B update. The value of "post-timestamp" will be set to the newer
90 timestamp plus one, so that the package can be pushed and applied.
Tao Bao4da324e2016-02-23 11:38:39 -080091
Doug Zongker1c390a22009-05-14 19:06:36 -070092 -e (--extra_script) <file>
93 Insert the contents of file at the end of the update script.
94
Hristo Bojinovdafb0422010-08-26 14:35:16 -070095 -a (--aslr_mode) <on|off>
96 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050097
Doug Zongker9b23f2c2013-11-25 14:44:12 -080098 -2 (--two_step)
99 Generate a 'two-step' OTA package, where recovery is updated
100 first, so that any changes made to the system partition are done
101 using the new recovery (new kernel, etc.).
102
Doug Zongker26e66192014-02-20 13:22:07 -0800103 --block
104 Generate a block-based OTA if possible. Will fall back to a
105 file-based OTA if the target_files is older and doesn't support
106 block-based OTAs.
107
Doug Zongker25568482014-03-03 10:21:27 -0800108 -b (--binary) <file>
109 Use the given binary as the update-binary in the output package,
110 instead of the binary in the build's target_files. Use for
111 development only.
112
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200113 -t (--worker_threads) <int>
114 Specifies the number of worker-threads that will be used when
115 generating patches for incremental updates (defaults to 3).
116
Tao Bao8dcf7382015-05-21 14:09:49 -0700117 --stash_threshold <float>
118 Specifies the threshold that will be used to compute the maximum
119 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800120
121 --gen_verify
122 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800123
124 --log_diff <file>
125 Generate a log file that shows the differences in the source and target
126 builds for an incremental package. This option is only meaningful when
127 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700128
129 --payload_signer <signer>
130 Specify the signer when signing the payload and metadata for A/B OTAs.
131 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
132 with the package private key. If the private key cannot be accessed
133 directly, a payload signer that knows how to do that should be specified.
134 The signer will be supplied with "-inkey <path_to_key>",
135 "-in <input_file>" and "-out <output_file>" parameters.
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700136
137 --payload_signer_args <args>
138 Specify the arguments needed for payload signer.
Doug Zongkereef39442009-04-02 12:14:19 -0700139"""
140
141import sys
142
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800143if sys.hexversion < 0x02070000:
144 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700145 sys.exit(1)
146
Doug Zongkerfc44a512014-08-26 13:10:25 -0700147import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700148import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800149import subprocess
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700150import shlex
Doug Zongkereef39442009-04-02 12:14:19 -0700151import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700152import zipfile
153
154import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700155import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700156import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700157
158OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700159OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700160OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700161OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700162OPTIONS.require_verbatim = set()
163OPTIONS.prohibit_verbatim = set(("system/build.prop",))
164OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700165OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700166OPTIONS.omit_prereq = False
Tao Bao4da324e2016-02-23 11:38:39 -0800167OPTIONS.downgrade = False
Tao Bao9f884e62017-02-28 11:48:48 -0800168OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700169OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700170OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700171OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
172if OPTIONS.worker_threads == 0:
173 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800174OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900175OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800176OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800177OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700178OPTIONS.oem_source = None
Tao Baodf4cb0b2016-02-25 19:49:55 -0800179OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700180OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700181OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700182OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700183# Stash size cannot exceed cache_size * threshold.
184OPTIONS.cache_size = None
185OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800186OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800187OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700188OPTIONS.payload_signer = None
Baligh Uddin2abbbd02016-06-22 12:14:16 -0700189OPTIONS.payload_signer_args = []
Tao Bao8dcf7382015-05-21 14:09:49 -0700190
Doug Zongkereef39442009-04-02 12:14:19 -0700191def MostPopularKey(d, default):
192 """Given a dict, return the key corresponding to the largest
193 value. Returns 'default' if the dict is empty."""
194 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700195 if not x:
196 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700197 x.sort()
198 return x[-1][1]
199
200
201def IsSymlink(info):
202 """Return true if the zipfile.ZipInfo object passed in represents a
203 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700204 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700205
Hristo Bojinov96be7202010-08-02 10:26:17 -0700206def IsRegular(info):
207 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700208 regular file."""
209 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700210
Michael Runge4038aa82013-12-13 18:06:28 -0800211def ClosestFileMatch(src, tgtfiles, existing):
212 """Returns the closest file match between a source file and list
213 of potential matches. The exact filename match is preferred,
214 then the sha1 is searched for, and finally a file with the same
215 basename is evaluated. Rename support in the updater-binary is
216 required for the latter checks to be used."""
217
218 result = tgtfiles.get("path:" + src.name)
219 if result is not None:
220 return result
221
222 if not OPTIONS.target_info_dict.get("update_rename_support", False):
223 return None
224
225 if src.size < 1000:
226 return None
227
228 result = tgtfiles.get("sha1:" + src.sha1)
229 if result is not None and existing.get(result.name) is None:
230 return result
231 result = tgtfiles.get("file:" + src.name.split("/")[-1])
232 if result is not None and existing.get(result.name) is None:
233 return result
234 return None
235
Dan Albert8b72aef2015-03-23 19:13:21 -0700236class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700237 def __init__(self, partition, fs_config):
238 self.partition = partition
239 self.fs_config = fs_config
240 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700241
Dan Albert8b72aef2015-03-23 19:13:21 -0700242 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700243 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700244 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700245 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700246
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700247 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700248 # The target_files contains a record of what the uid,
249 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700250 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700251
252 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700253 if not line:
254 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700255 columns = line.split()
256 name, uid, gid, mode = columns[:4]
257 selabel = None
258 capabilities = None
259
260 # After the first 4 columns, there are a series of key=value
261 # pairs. Extract out the fields we care about.
262 for element in columns[4:]:
263 key, value = element.split("=")
264 if key == "selabel":
265 selabel = value
266 if key == "capabilities":
267 capabilities = value
268
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700269 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700270 if i is not None:
271 i.uid = int(uid)
272 i.gid = int(gid)
273 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700274 i.selabel = selabel
275 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700276 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700277 i.children.sort(key=lambda i: i.name)
278
Tao Baof2cffbd2015-07-22 12:33:18 -0700279 # Set metadata for the files generated by this script. For full recovery
280 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700281 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700282 if i:
283 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700284 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700285 if i:
286 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700287
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700288
Dan Albert8b72aef2015-03-23 19:13:21 -0700289class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700290 """Items represent the metadata (user, group, mode) of files and
291 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700292 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700293 self.itemset = itemset
294 self.name = name
295 self.uid = None
296 self.gid = None
297 self.mode = None
298 self.selabel = None
299 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700300 self.is_dir = is_dir
301 self.descendants = None
302 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700303
304 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700305 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700306 self.parent.children.append(self)
307 else:
308 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700309 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700310 self.children = []
311
312 def Dump(self, indent=0):
313 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700314 print "%s%s %d %d %o" % (
315 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700316 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700317 print "%s%s %s %s %s" % (
318 " " * indent, self.name, self.uid, self.gid, self.mode)
319 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700320 print "%s%s" % (" "*indent, self.descendants)
321 print "%s%s" % (" "*indent, self.best_subtree)
322 for i in self.children:
323 i.Dump(indent=indent+1)
324
Doug Zongkereef39442009-04-02 12:14:19 -0700325 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700326 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700327 all children and determine the best strategy for using set_perm_recursive
328 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700329 values. Recursively calls itself for all descendants.
330
Dan Albert8b72aef2015-03-23 19:13:21 -0700331 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
332 counting up all descendants of this node. (dmode or fmode may be None.)
333 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
334 fmode, selabel, capabilities) tuple that will match the most descendants of
335 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700336 """
337
Dan Albert8b72aef2015-03-23 19:13:21 -0700338 assert self.is_dir
339 key = (self.uid, self.gid, self.mode, None, self.selabel,
340 self.capabilities)
341 self.descendants = {key: 1}
342 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700343 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700344 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700345 for k, v in i.CountChildMetadata().iteritems():
346 d[k] = d.get(k, 0) + v
347 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700348 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700349 d[k] = d.get(k, 0) + 1
350
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700351 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
352 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700353
354 # First, find the (uid, gid) pair that matches the most
355 # descendants.
356 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700357 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700358 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
359 ug = MostPopularKey(ug, (0, 0))
360
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700361 # Now find the dmode, fmode, selabel, and capabilities that match
362 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700363 best_dmode = (0, 0o755)
364 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700365 best_selabel = (0, None)
366 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700367 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700368 if k[:2] != ug:
369 continue
370 if k[2] is not None and count >= best_dmode[0]:
371 best_dmode = (count, k[2])
372 if k[3] is not None and count >= best_fmode[0]:
373 best_fmode = (count, k[3])
374 if k[4] is not None and count >= best_selabel[0]:
375 best_selabel = (count, k[4])
376 if k[5] is not None and count >= best_capabilities[0]:
377 best_capabilities = (count, k[5])
378 self.best_subtree = ug + (
379 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700380
381 return d
382
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700383 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700384 """Append set_perm/set_perm_recursive commands to 'script' to
385 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700386 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700387
388 self.CountChildMetadata()
389
390 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700391 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
392 # that the current item (and all its children) have already been set to.
393 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700394 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700395 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700396 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700397 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700398 current = item.best_subtree
399
400 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700401 item.mode != current[2] or item.selabel != current[4] or \
402 item.capabilities != current[5]:
403 script.SetPermissions("/"+item.name, item.uid, item.gid,
404 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700405
406 for i in item.children:
407 recurse(i, current)
408 else:
409 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700410 item.mode != current[3] or item.selabel != current[4] or \
411 item.capabilities != current[5]:
412 script.SetPermissions("/"+item.name, item.uid, item.gid,
413 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700414
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700415 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700416
417
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700418def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
419 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700420 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800421 list of symlinks. output_zip may be None, in which case the copy is
422 skipped (but the other side effects still happen). substitute is an
423 optional dict of {output filename: contents} to be output instead of
424 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700425 """
426
427 symlinks = []
428
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700429 partition = itemset.partition
430
Doug Zongkereef39442009-04-02 12:14:19 -0700431 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700432 prefix = partition.upper() + "/"
433 if info.filename.startswith(prefix):
434 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700435 if IsSymlink(info):
436 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700437 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700438 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700439 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700440 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700441 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700442 if substitute and fn in substitute and substitute[fn] is None:
443 continue
444 if output_zip is not None:
445 if substitute and fn in substitute:
446 data = substitute[fn]
447 else:
448 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700449 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700450 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700451 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700452 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700453 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700454
455 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800456 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700457
458
Doug Zongkereef39442009-04-02 12:14:19 -0700459def SignOutput(temp_zip_name, output_zip_name):
460 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
461 pw = key_passwords[OPTIONS.package_key]
462
Doug Zongker951495f2009-08-14 12:44:19 -0700463 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
464 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700465
466
Dan Albert8b72aef2015-03-23 19:13:21 -0700467def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700468 oem_props = info_dict.get("oem_fingerprint_properties")
Michael Runge560569a2014-09-18 15:12:45 -0700469 if oem_props is None or len(oem_props) == 0:
Michael Runge6e836112014-04-15 17:40:21 -0700470 device = GetBuildProp("ro.product.device", info_dict)
471 script.AssertDevice(device)
472 else:
473 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700474 raise common.ExternalError(
475 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700476 for prop in oem_props.split():
477 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700478 raise common.ExternalError(
479 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700480 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700481
Doug Zongkereef39442009-04-02 12:14:19 -0700482
Tao Bao47ec5ab2016-11-30 12:11:57 -0800483def _WriteRecoveryImageToBoot(script, output_zip):
484 """Find and write recovery image to /boot in two-step OTA.
485
486 In two-step OTAs, we write recovery image to /boot as the first step so that
487 we can reboot to there and install a new recovery image to /recovery.
488 A special "recovery-two-step.img" will be preferred, which encodes the correct
489 path of "/boot". Otherwise the device may show "device is corrupt" message
490 when booting into /boot.
491
492 Fall back to using the regular recovery.img if the two-step recovery image
493 doesn't exist. Note that rebuilding the special image at this point may be
494 infeasible, because we don't have the desired boot signer and keys when
495 calling ota_from_target_files.py.
496 """
497
498 recovery_two_step_img_name = "recovery-two-step.img"
499 recovery_two_step_img_path = os.path.join(
500 OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
501 if os.path.exists(recovery_two_step_img_path):
502 recovery_two_step_img = common.GetBootableImage(
503 recovery_two_step_img_name, recovery_two_step_img_name,
504 OPTIONS.input_tmp, "RECOVERY")
505 common.ZipWriteStr(
506 output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
507 print "two-step package: using %s in stage 1/3" % (
508 recovery_two_step_img_name,)
509 script.WriteRawImage("/boot", recovery_two_step_img_name)
510 else:
511 print "two-step package: using recovery.img in stage 1/3"
512 # The "recovery.img" entry has been written into package earlier.
513 script.WriteRawImage("/boot", "recovery.img")
514
515
Doug Zongkerc9253822014-02-04 12:17:58 -0800516def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700517 namelist = [name for name in target_files_zip.namelist()]
518 return ("SYSTEM/recovery-from-boot.p" in namelist or
519 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700520
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700521def HasVendorPartition(target_files_zip):
522 try:
523 target_files_zip.getinfo("VENDOR/")
524 return True
525 except KeyError:
526 return False
527
Michael Runge6e836112014-04-15 17:40:21 -0700528def GetOemProperty(name, oem_props, oem_dict, info_dict):
529 if oem_props is not None and name in oem_props:
530 return oem_dict[name]
531 return GetBuildProp(name, info_dict)
532
533
534def CalculateFingerprint(oem_props, oem_dict, info_dict):
535 if oem_props is None:
536 return GetBuildProp("ro.build.fingerprint", info_dict)
537 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700538 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
539 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
540 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
541 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700542
Doug Zongkerfc44a512014-08-26 13:10:25 -0700543
Doug Zongker3c84f562014-07-31 11:06:30 -0700544def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700545 # Return an image object (suitable for passing to BlockImageDiff)
546 # for the 'which' partition (most be "system" or "vendor"). If a
547 # prebuilt image and file map are found in tmpdir they are used,
548 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700549
550 assert which in ("system", "vendor")
551
552 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700553 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
554 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700555 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700556 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700557
558 else:
559 print "building %s.img from target-files" % (which,)
560
561 # This is an 'old' target-files, which does not contain images
562 # already built. Build them.
563
Doug Zongkerfc44a512014-08-26 13:10:25 -0700564 mappath = tempfile.mkstemp()[1]
565 OPTIONS.tempfiles.append(mappath)
566
Doug Zongker3c84f562014-07-31 11:06:30 -0700567 import add_img_to_target_files
568 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700569 path = add_img_to_target_files.BuildSystem(
570 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700571 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700572 path = add_img_to_target_files.BuildVendor(
573 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700574
Tao Baoff777812015-05-12 11:42:31 -0700575 # Bug: http://b/20939131
576 # In ext4 filesystems, block 0 might be changed even being mounted
577 # R/O. We add it to clobbered_blocks so that it will be written to the
578 # target unconditionally. Note that they are still part of care_map.
579 clobbered_blocks = "0"
580
581 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700582
583
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700584def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700585 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700586 # be installed on top of. For now, we expect the API just won't
587 # change very often. Similarly for fstab, it might have changed
588 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700589 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700590
Michael Runge6e836112014-04-15 17:40:21 -0700591 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700592 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700593 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700594 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700595 if OPTIONS.oem_source is None:
596 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800597 if not OPTIONS.oem_no_mount:
598 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700599 oem_dict = common.LoadDictionaryFromLines(
600 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700601
Dan Albert8b72aef2015-03-23 19:13:21 -0700602 metadata = {
603 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700604 OPTIONS.info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700605 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
606 OPTIONS.info_dict),
607 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
608 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700609
Doug Zongker05d3dea2009-06-22 11:32:31 -0700610 device_specific = common.DeviceSpecificParams(
611 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700612 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700613 output_zip=output_zip,
614 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700615 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700616 metadata=metadata,
617 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700618
Doug Zongkerc9253822014-02-04 12:17:58 -0800619 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800620 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800621
Tao Baob4cfca52016-02-04 14:26:02 -0800622 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
623
Doug Zongker962069c2009-04-23 11:41:58 -0700624 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700625 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700626 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
627 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700628
Michael Runge6e836112014-04-15 17:40:21 -0700629 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700630 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800631
632 # Two-step package strategy (in chronological order, which is *not*
633 # the order in which the generated script has things):
634 #
635 # if stage is not "2/3" or "3/3":
636 # write recovery image to boot partition
637 # set stage to "2/3"
638 # reboot to boot partition and restart recovery
639 # else if stage is "2/3":
640 # write recovery image to recovery partition
641 # set stage to "3/3"
642 # reboot to recovery partition and restart recovery
643 # else:
644 # (stage must be "3/3")
645 # set stage to ""
646 # do normal full package installation:
647 # wipe and install system, boot image, etc.
648 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700649 # complete script normally
650 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800651
652 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
653 OPTIONS.input_tmp, "RECOVERY")
654 if OPTIONS.two_step:
655 if not OPTIONS.info_dict.get("multistage_support", None):
656 assert False, "two-step packages not supported by this build"
657 fs = OPTIONS.info_dict["fstab"]["/misc"]
658 assert fs.fs_type.upper() == "EMMC", \
659 "two-step packages only supported on devices with EMMC /misc partitions"
660 bcb_dev = {"bcb_dev": fs.device}
661 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
662 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700663if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800664""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -0800665
666 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
667 script.Comment("Stage 2/3")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800668 script.WriteRawImage("/recovery", "recovery.img")
669 script.AppendExtra("""
670set_stage("%(bcb_dev)s", "3/3");
671reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700672else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800673""" % bcb_dev)
674
Tao Bao47ec5ab2016-11-30 12:11:57 -0800675 # Stage 3/3: Make changes.
676 script.Comment("Stage 3/3")
677
Tao Bao6c55a8a2015-04-08 15:30:27 -0700678 # Dump fingerprints
679 script.Print("Target: %s" % CalculateFingerprint(
680 oem_props, oem_dict, OPTIONS.info_dict))
681
Doug Zongkere5ff5902012-01-17 10:55:37 -0800682 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700683
Doug Zongker01ce19c2014-02-04 13:48:15 -0800684 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700685
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700686 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800687 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700688 if HasVendorPartition(input_zip):
689 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700690
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400691 # Place a copy of file_contexts.bin into the OTA package which will be used
692 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700693 if "selinux_fc" in OPTIONS.info_dict:
694 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500695
Michael Runge7cd99ba2014-10-22 17:21:48 -0700696 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
697
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700698 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700699 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800700
Doug Zongker26e66192014-02-20 13:22:07 -0800701 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700702 # Full OTA is done as an "incremental" against an empty source
703 # image. This has the effect of writing new data from the package
704 # to the entire partition, but lets us reuse the updater code that
705 # writes incrementals to do it.
706 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
707 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700708 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700709 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800710 else:
711 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700712 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800713 if not has_recovery_patch:
714 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800715 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700716
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700717 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800718 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700719
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700720 boot_img = common.GetBootableImage(
721 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800722
Doug Zongker91a99c22014-05-09 13:15:01 -0700723 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800724 def output_sink(fn, data):
725 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700726 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800727
728 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
729 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700730
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700731 system_items.GetMetadata(input_zip)
732 system_items.Get("system").SetPermissions(script)
733
734 if HasVendorPartition(input_zip):
735 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
736 script.ShowProgress(0.1, 0)
737
738 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700739 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
740 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700741 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700742 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700743 else:
744 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700745 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700746 script.UnpackPackageDir("vendor", "/vendor")
747
748 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
749 script.MakeSymlinks(symlinks)
750
751 vendor_items.GetMetadata(input_zip)
752 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700753
Doug Zongker37974732010-09-16 17:44:38 -0700754 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700755 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700756
Doug Zongker01ce19c2014-02-04 13:48:15 -0800757 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700758 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700759
Doug Zongker01ce19c2014-02-04 13:48:15 -0800760 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700761 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700762
Doug Zongker1c390a22009-05-14 19:06:36 -0700763 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700764 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700765
Doug Zongker14833602010-02-02 13:12:04 -0800766 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800767
Doug Zongker922206e2014-03-04 13:16:24 -0800768 if OPTIONS.wipe_user_data:
769 script.ShowProgress(0.1, 10)
770 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700771
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800772 if OPTIONS.two_step:
773 script.AppendExtra("""
774set_stage("%(bcb_dev)s", "");
775""" % bcb_dev)
776 script.AppendExtra("else\n")
Tao Bao47ec5ab2016-11-30 12:11:57 -0800777
778 # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
779 script.Comment("Stage 1/3")
780 _WriteRecoveryImageToBoot(script, output_zip)
781
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800782 script.AppendExtra("""
783set_stage("%(bcb_dev)s", "2/3");
784reboot_now("%(bcb_dev)s", "");
785endif;
786endif;
787""" % bcb_dev)
Tao Baob4cfca52016-02-04 14:26:02 -0800788
Tao Bao4da324e2016-02-23 11:38:39 -0800789 script.SetProgress(1)
790 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -0800791 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700792 WriteMetadata(metadata, output_zip)
793
Doug Zongkerfc44a512014-08-26 13:10:25 -0700794
Dan Albert8e0178d2015-01-27 15:53:15 -0800795def WritePolicyConfig(file_name, output_zip):
796 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500797
Doug Zongker2ea21062010-04-28 16:05:21 -0700798
799def WriteMetadata(metadata, output_zip):
800 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
801 "".join(["%s=%s\n" % kv
802 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700803
Doug Zongkerfc44a512014-08-26 13:10:25 -0700804
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700805def LoadPartitionFiles(z, partition):
806 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700807 ZipFile, and return a dict of {filename: File object}."""
808 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700809 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700810 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700811 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700812 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700813 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700814 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700815 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800816 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700817
818
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700819def GetBuildProp(prop, info_dict):
820 """Return the fingerprint of the build of a given target-files info_dict."""
821 try:
822 return info_dict.get("build.prop", {})[prop]
823 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700824 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700825
Doug Zongkerfc44a512014-08-26 13:10:25 -0700826
Michael Runge4038aa82013-12-13 18:06:28 -0800827def AddToKnownPaths(filename, known_paths):
828 if filename[-1] == "/":
829 return
830 dirs = filename.split("/")[:-1]
831 while len(dirs) > 0:
832 path = "/".join(dirs)
833 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700834 break
Michael Runge4038aa82013-12-13 18:06:28 -0800835 known_paths.add(path)
836 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700837
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700838
Tao Bao99c17252017-02-07 11:21:17 -0800839def HandleDowngradeMetadata(metadata):
840 # Only incremental OTAs are allowed to reach here.
841 assert OPTIONS.incremental_source is not None
842
843 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
844 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
845 is_downgrade = long(post_timestamp) < long(pre_timestamp)
846
847 if OPTIONS.downgrade:
Tao Bao99c17252017-02-07 11:21:17 -0800848 if not is_downgrade:
849 raise RuntimeError("--downgrade specified but no downgrade detected: "
850 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao9f884e62017-02-28 11:48:48 -0800851 metadata["ota-downgrade"] = "yes"
852 elif OPTIONS.timestamp:
853 if not is_downgrade:
854 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
855 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
856 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Bao99c17252017-02-07 11:21:17 -0800857 else:
858 if is_downgrade:
Tao Bao9f884e62017-02-28 11:48:48 -0800859 raise RuntimeError("Downgrade detected based on timestamp check: "
860 "pre: %s, post: %s. Need to specify --timestamp OR "
861 "--downgrade to allow building the incremental." % (
862 pre_timestamp, post_timestamp))
Tao Bao99c17252017-02-07 11:21:17 -0800863 metadata["post-timestamp"] = post_timestamp
864
865
Geremy Condra36bd3652014-02-06 19:45:10 -0800866def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700867 # TODO(tbao): We should factor out the common parts between
868 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800869 source_version = OPTIONS.source_info_dict["recovery_api_version"]
870 target_version = OPTIONS.target_info_dict["recovery_api_version"]
871
872 if source_version == 0:
873 print ("WARNING: generating edify script for a source that "
874 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700875 script = edify_generator.EdifyGenerator(
876 source_version, OPTIONS.target_info_dict,
877 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800878
Tao Bao3806c232015-07-05 21:08:33 -0700879 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
880 recovery_mount_options = OPTIONS.source_info_dict.get(
881 "recovery_mount_options")
882 oem_dict = None
883 if oem_props is not None and len(oem_props) > 0:
884 if OPTIONS.oem_source is None:
885 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800886 if not OPTIONS.oem_no_mount:
887 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700888 oem_dict = common.LoadDictionaryFromLines(
889 open(OPTIONS.oem_source).readlines())
890
Dan Albert8b72aef2015-03-23 19:13:21 -0700891 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700892 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
893 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -0800894 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700895 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800896
Tao Bao99c17252017-02-07 11:21:17 -0800897 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -0800898
Geremy Condra36bd3652014-02-06 19:45:10 -0800899 device_specific = common.DeviceSpecificParams(
900 source_zip=source_zip,
901 source_version=source_version,
902 target_zip=target_zip,
903 target_version=target_version,
904 output_zip=output_zip,
905 script=script,
906 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700907 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800908
Tao Bao3806c232015-07-05 21:08:33 -0700909 source_fp = CalculateFingerprint(oem_props, oem_dict,
910 OPTIONS.source_info_dict)
911 target_fp = CalculateFingerprint(oem_props, oem_dict,
912 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800913 metadata["pre-build"] = source_fp
914 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700915 metadata["pre-build-incremental"] = GetBuildProp(
916 "ro.build.version.incremental", OPTIONS.source_info_dict)
917 metadata["post-build-incremental"] = GetBuildProp(
918 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800919
920 source_boot = common.GetBootableImage(
921 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
922 OPTIONS.source_info_dict)
923 target_boot = common.GetBootableImage(
924 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
925 updating_boot = (not OPTIONS.two_step and
926 (source_boot.data != target_boot.data))
927
Geremy Condra36bd3652014-02-06 19:45:10 -0800928 target_recovery = common.GetBootableImage(
929 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800930
Doug Zongkerfc44a512014-08-26 13:10:25 -0700931 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
932 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700933
934 blockimgdiff_version = 1
935 if OPTIONS.info_dict:
936 blockimgdiff_version = max(
937 int(i) for i in
938 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
939
Tao Baof8acad12016-07-07 09:09:58 -0700940 # Check the first block of the source system partition for remount R/W only
941 # if the filesystem is ext4.
942 system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
943 check_first_block = system_src_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700944 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
945 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
946 # b) the blocks listed in block map may not contain all the bytes for a given
947 # file (because they're rounded to be 4K-aligned).
Tao Baof8acad12016-07-07 09:09:58 -0700948 system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
949 disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
950 system_tgt_partition.fs_type == "squashfs")
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700951 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800952 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700953 version=blockimgdiff_version,
954 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700955
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700956 if HasVendorPartition(target_zip):
957 if not HasVendorPartition(source_zip):
958 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700959 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
960 OPTIONS.source_info_dict)
961 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
962 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800963
964 # Check first block of vendor partition for remount R/W only if
965 # disk type is ext4
966 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baob4cfca52016-02-04 14:26:02 -0800967 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700968 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700969 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800970 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700971 version=blockimgdiff_version,
972 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700973 else:
974 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800975
Michael Rungec6e3afd2014-05-05 11:55:47 -0700976 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800977 device_specific.IncrementalOTA_Assertions()
978
979 # Two-step incremental package strategy (in chronological order,
980 # which is *not* the order in which the generated script has
981 # things):
982 #
983 # if stage is not "2/3" or "3/3":
984 # do verification on current system
985 # write recovery image to boot partition
986 # set stage to "2/3"
987 # reboot to boot partition and restart recovery
988 # else if stage is "2/3":
989 # write recovery image to recovery partition
990 # set stage to "3/3"
991 # reboot to recovery partition and restart recovery
992 # else:
993 # (stage must be "3/3")
994 # perform update:
995 # patch system files, etc.
996 # force full install of new boot image
997 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700998 # complete script normally
999 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -08001000
1001 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001002 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -08001003 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001004 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -08001005 assert fs.fs_type.upper() == "EMMC", \
1006 "two-step packages only supported on devices with EMMC /misc partitions"
1007 bcb_dev = {"bcb_dev": fs.device}
1008 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1009 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001010if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001011""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -08001012
1013 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1014 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001015 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -08001016 script.WriteRawImage("/recovery", "recovery.img")
1017 script.AppendExtra("""
1018set_stage("%(bcb_dev)s", "3/3");
1019reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001020else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -08001021""" % bcb_dev)
1022
Tao Bao47ec5ab2016-11-30 12:11:57 -08001023 # Stage 1/3: (a) Verify the current system.
1024 script.Comment("Stage 1/3")
1025
Tao Bao6c55a8a2015-04-08 15:30:27 -07001026 # Dump fingerprints
1027 script.Print("Source: %s" % CalculateFingerprint(
1028 oem_props, oem_dict, OPTIONS.source_info_dict))
1029 script.Print("Target: %s" % CalculateFingerprint(
1030 oem_props, oem_dict, OPTIONS.target_info_dict))
1031
Geremy Condra36bd3652014-02-06 19:45:10 -08001032 script.Print("Verifying current system...")
1033
1034 device_specific.IncrementalOTA_VerifyBegin()
1035
Michael Rungec6e3afd2014-05-05 11:55:47 -07001036 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -07001037 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
1038 # patching on a device that's already on the target build will damage the
1039 # system. Because operations like move don't check the block state, they
1040 # always apply the changes unconditionally.
1041 if blockimgdiff_version <= 2:
1042 script.AssertSomeFingerprint(source_fp)
1043 else:
1044 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -07001045 else:
Tao Baodd2a5892015-03-12 12:32:37 -07001046 if blockimgdiff_version <= 2:
1047 script.AssertSomeThumbprint(
1048 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1049 else:
1050 script.AssertSomeThumbprint(
1051 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1052 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -08001053
Tao Baob4cfca52016-02-04 14:26:02 -08001054 # Check the required cache size (i.e. stashed blocks).
1055 size = []
1056 if system_diff:
1057 size.append(system_diff.required_cache)
1058 if vendor_diff:
1059 size.append(vendor_diff.required_cache)
1060
Geremy Condra36bd3652014-02-06 19:45:10 -08001061 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -07001062 boot_type, boot_device = common.GetTypeAndDevice(
1063 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -08001064 d = common.Difference(target_boot, source_boot)
1065 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001066 if d is None:
1067 include_full_boot = True
1068 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1069 else:
1070 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001071
Doug Zongkerf8340082014-08-05 10:39:37 -07001072 print "boot target: %d source: %d diff: %d" % (
1073 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001074
Doug Zongkerf8340082014-08-05 10:39:37 -07001075 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001076
Doug Zongkerf8340082014-08-05 10:39:37 -07001077 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1078 (boot_type, boot_device,
1079 source_boot.size, source_boot.sha1,
1080 target_boot.size, target_boot.sha1))
Tao Baob4cfca52016-02-04 14:26:02 -08001081 size.append(target_boot.size)
1082
1083 if size:
1084 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001085
1086 device_specific.IncrementalOTA_VerifyEnd()
1087
1088 if OPTIONS.two_step:
Tao Bao47ec5ab2016-11-30 12:11:57 -08001089 # Stage 1/3: (b) Write recovery image to /boot.
1090 _WriteRecoveryImageToBoot(script, output_zip)
1091
Geremy Condra36bd3652014-02-06 19:45:10 -08001092 script.AppendExtra("""
1093set_stage("%(bcb_dev)s", "2/3");
1094reboot_now("%(bcb_dev)s", "");
1095else
1096""" % bcb_dev)
1097
Tao Bao47ec5ab2016-11-30 12:11:57 -08001098 # Stage 3/3: Make changes.
1099 script.Comment("Stage 3/3")
1100
Jesse Zhao75bcea02015-01-06 10:59:53 -08001101 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001102 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001103 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001104 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001105
Geremy Condra36bd3652014-02-06 19:45:10 -08001106 script.Comment("---- start making changes here ----")
1107
1108 device_specific.IncrementalOTA_InstallBegin()
1109
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001110 system_diff.WriteScript(script, output_zip,
1111 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001112
Doug Zongkerfc44a512014-08-26 13:10:25 -07001113 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001114 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001115
1116 if OPTIONS.two_step:
1117 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1118 script.WriteRawImage("/boot", "boot.img")
1119 print "writing full boot image (forced by two-step mode)"
1120
1121 if not OPTIONS.two_step:
1122 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001123 if include_full_boot:
1124 print "boot image changed; including full."
1125 script.Print("Installing boot image...")
1126 script.WriteRawImage("/boot", "boot.img")
1127 else:
1128 # Produce the boot image by applying a patch to the current
1129 # contents of the boot partition, and write it back to the
1130 # partition.
1131 print "boot image changed; including patch."
1132 script.Print("Patching boot image...")
1133 script.ShowProgress(0.1, 10)
1134 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1135 % (boot_type, boot_device,
1136 source_boot.size, source_boot.sha1,
1137 target_boot.size, target_boot.sha1),
1138 "-",
1139 target_boot.size, target_boot.sha1,
1140 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001141 else:
1142 print "boot image unchanged; skipping."
1143
1144 # Do device-specific installation (eg, write radio image).
1145 device_specific.IncrementalOTA_InstallEnd()
1146
1147 if OPTIONS.extra_script is not None:
1148 script.AppendExtra(OPTIONS.extra_script)
1149
Doug Zongker922206e2014-03-04 13:16:24 -08001150 if OPTIONS.wipe_user_data:
1151 script.Print("Erasing user data...")
1152 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001153 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001154
Geremy Condra36bd3652014-02-06 19:45:10 -08001155 if OPTIONS.two_step:
1156 script.AppendExtra("""
1157set_stage("%(bcb_dev)s", "");
1158endif;
1159endif;
1160""" % bcb_dev)
1161
1162 script.SetProgress(1)
Tao Baofa41fb22016-03-08 17:53:39 -08001163 # For downgrade OTAs, we prefer to use the update-binary in the source
1164 # build that is actually newer than the one in the target build.
1165 if OPTIONS.downgrade:
1166 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1167 else:
1168 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001169 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001170 WriteMetadata(metadata, output_zip)
1171
Doug Zongker32b527d2014-03-04 10:03:02 -08001172
Tao Bao9bc6bb22015-11-09 16:58:28 -08001173def WriteVerifyPackage(input_zip, output_zip):
1174 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1175
1176 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1177 recovery_mount_options = OPTIONS.info_dict.get(
1178 "recovery_mount_options")
1179 oem_dict = None
1180 if oem_props is not None and len(oem_props) > 0:
1181 if OPTIONS.oem_source is None:
1182 raise common.ExternalError("OEM source required for this build")
1183 script.Mount("/oem", recovery_mount_options)
1184 oem_dict = common.LoadDictionaryFromLines(
1185 open(OPTIONS.oem_source).readlines())
1186
1187 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1188 metadata = {
1189 "post-build": target_fp,
1190 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1191 OPTIONS.info_dict),
1192 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1193 }
1194
1195 device_specific = common.DeviceSpecificParams(
1196 input_zip=input_zip,
1197 input_version=OPTIONS.info_dict["recovery_api_version"],
1198 output_zip=output_zip,
1199 script=script,
1200 input_tmp=OPTIONS.input_tmp,
1201 metadata=metadata,
1202 info_dict=OPTIONS.info_dict)
1203
1204 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1205
1206 script.Print("Verifying device images against %s..." % target_fp)
1207 script.AppendExtra("")
1208
1209 script.Print("Verifying boot...")
1210 boot_img = common.GetBootableImage(
1211 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1212 boot_type, boot_device = common.GetTypeAndDevice(
1213 "/boot", OPTIONS.info_dict)
1214 script.Verify("%s:%s:%d:%s" % (
1215 boot_type, boot_device, boot_img.size, boot_img.sha1))
1216 script.AppendExtra("")
1217
1218 script.Print("Verifying recovery...")
1219 recovery_img = common.GetBootableImage(
1220 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1221 recovery_type, recovery_device = common.GetTypeAndDevice(
1222 "/recovery", OPTIONS.info_dict)
1223 script.Verify("%s:%s:%d:%s" % (
1224 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1225 script.AppendExtra("")
1226
1227 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1228 system_tgt.ResetFileMap()
1229 system_diff = common.BlockDifference("system", system_tgt, src=None)
1230 system_diff.WriteStrictVerifyScript(script)
1231
1232 if HasVendorPartition(input_zip):
1233 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1234 vendor_tgt.ResetFileMap()
1235 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1236 vendor_diff.WriteStrictVerifyScript(script)
1237
1238 # Device specific partitions, such as radio, bootloader and etc.
1239 device_specific.VerifyOTA_Assertions()
1240
1241 script.SetProgress(1.0)
1242 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001243 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001244 WriteMetadata(metadata, output_zip)
1245
1246
Tao Baoc098e9e2016-01-07 13:03:56 -08001247def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1248 source_file=None):
1249 """Generate an Android OTA package that has A/B update payload."""
1250
1251 # Setup signing keys.
1252 if OPTIONS.package_key is None:
1253 OPTIONS.package_key = OPTIONS.info_dict.get(
1254 "default_system_dev_certificate",
1255 "build/target/product/security/testkey")
1256
Tao Baodea0f8b2016-06-20 17:55:06 -07001257 # A/B updater expects a signing key in RSA format. Gets the key ready for
1258 # later use in step 3, unless a payload_signer has been specified.
1259 if OPTIONS.payload_signer is None:
1260 cmd = ["openssl", "pkcs8",
1261 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1262 "-inform", "DER", "-nocrypt"]
1263 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1264 cmd.extend(["-out", rsa_key])
1265 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1266 p1.wait()
1267 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001268
Tao Baodea0f8b2016-06-20 17:55:06 -07001269 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001270 temp_zip_file = tempfile.NamedTemporaryFile()
1271 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1272 compression=zipfile.ZIP_DEFLATED)
1273
1274 # Metadata to comply with Android OTA package format.
1275 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1276 oem_dict = None
1277 if oem_props:
1278 if OPTIONS.oem_source is None:
1279 raise common.ExternalError("OEM source required for this build")
1280 oem_dict = common.LoadDictionaryFromLines(
1281 open(OPTIONS.oem_source).readlines())
1282
1283 metadata = {
1284 "post-build": CalculateFingerprint(oem_props, oem_dict,
1285 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001286 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1287 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001288 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1289 OPTIONS.info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001290 "ota-required-cache": "0",
1291 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001292 }
1293
1294 if source_file is not None:
1295 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1296 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001297 metadata["pre-build-incremental"] = GetBuildProp(
1298 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001299
Tao Bao99c17252017-02-07 11:21:17 -08001300 HandleDowngradeMetadata(metadata)
1301 else:
1302 metadata["post-timestamp"] = GetBuildProp(
1303 "ro.build.date.utc", OPTIONS.info_dict)
1304
Tao Baoc098e9e2016-01-07 13:03:56 -08001305 # 1. Generate payload.
1306 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1307 cmd = ["brillo_update_payload", "generate",
1308 "--payload", payload_file,
1309 "--target_image", target_file]
1310 if source_file is not None:
1311 cmd.extend(["--source_image", source_file])
1312 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1313 p1.wait()
1314 assert p1.returncode == 0, "brillo_update_payload generate failed"
1315
1316 # 2. Generate hashes of the payload and metadata files.
1317 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1318 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1319 cmd = ["brillo_update_payload", "hash",
1320 "--unsigned_payload", payload_file,
1321 "--signature_size", "256",
1322 "--metadata_hash_file", metadata_sig_file,
1323 "--payload_hash_file", payload_sig_file]
1324 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1325 p1.wait()
1326 assert p1.returncode == 0, "brillo_update_payload hash failed"
1327
1328 # 3. Sign the hashes and insert them back into the payload file.
1329 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1330 suffix=".bin")
1331 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1332 suffix=".bin")
1333 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001334 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001335 cmd = [OPTIONS.payload_signer]
1336 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001337 else:
1338 cmd = ["openssl", "pkeyutl", "-sign",
1339 "-inkey", rsa_key,
1340 "-pkeyopt", "digest:sha256"]
1341 cmd.extend(["-in", payload_sig_file,
1342 "-out", signed_payload_sig_file])
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001343
Tao Baoc098e9e2016-01-07 13:03:56 -08001344 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1345 p1.wait()
1346 assert p1.returncode == 0, "openssl sign payload failed"
1347
1348 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001349 if OPTIONS.payload_signer is not None:
Baligh Uddin2abbbd02016-06-22 12:14:16 -07001350 cmd = [OPTIONS.payload_signer]
1351 cmd.extend(OPTIONS.payload_signer_args)
Tao Baodea0f8b2016-06-20 17:55:06 -07001352 else:
1353 cmd = ["openssl", "pkeyutl", "-sign",
1354 "-inkey", rsa_key,
1355 "-pkeyopt", "digest:sha256"]
1356 cmd.extend(["-in", metadata_sig_file,
1357 "-out", signed_metadata_sig_file])
Tao Baoc098e9e2016-01-07 13:03:56 -08001358 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1359 p1.wait()
1360 assert p1.returncode == 0, "openssl sign metadata failed"
1361
1362 # 3c. Insert the signatures back into the payload file.
1363 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1364 suffix=".bin")
1365 cmd = ["brillo_update_payload", "sign",
1366 "--unsigned_payload", payload_file,
1367 "--payload", signed_payload_file,
1368 "--signature_size", "256",
1369 "--metadata_signature_file", signed_metadata_sig_file,
1370 "--payload_signature_file", signed_payload_sig_file]
1371 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1372 p1.wait()
1373 assert p1.returncode == 0, "brillo_update_payload sign failed"
1374
Alex Deymo19241c12016-02-04 22:29:29 -08001375 # 4. Dump the signed payload properties.
1376 properties_file = common.MakeTempFile(prefix="payload-properties-",
1377 suffix=".txt")
1378 cmd = ["brillo_update_payload", "properties",
1379 "--payload", signed_payload_file,
1380 "--properties_file", properties_file]
1381 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1382 p1.wait()
1383 assert p1.returncode == 0, "brillo_update_payload properties failed"
1384
Tao Bao38ca0be2016-06-14 17:48:11 -07001385 if OPTIONS.wipe_user_data:
1386 with open(properties_file, "a") as f:
1387 f.write("POWERWASH=1\n")
1388 metadata["ota-wipe"] = "yes"
1389
Alex Deymo19241c12016-02-04 22:29:29 -08001390 # Add the signed payload file and properties into the zip.
1391 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001392 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1393 compress_type=zipfile.ZIP_STORED)
1394 WriteMetadata(metadata, output_zip)
1395
Tianjie Xucfa86222016-03-07 16:31:19 -08001396 # If dm-verity is supported for the device, copy contents of care_map
1397 # into A/B OTA package.
1398 if OPTIONS.info_dict.get("verity") == "true":
1399 target_zip = zipfile.ZipFile(target_file, "r")
1400 care_map_path = "META/care_map.txt"
1401 namelist = target_zip.namelist()
1402 if care_map_path in namelist:
1403 care_map_data = target_zip.read(care_map_path)
1404 common.ZipWriteStr(output_zip, "care_map.txt", care_map_data)
1405 else:
1406 print "Warning: cannot find care map file in target_file package"
1407 common.ZipClose(target_zip)
1408
Tao Baoc098e9e2016-01-07 13:03:56 -08001409 # Sign the whole package to comply with the Android OTA package format.
1410 common.ZipClose(output_zip)
1411 SignOutput(temp_zip_file.name, output_file)
1412 temp_zip_file.close()
1413
1414
Dan Albert8b72aef2015-03-23 19:13:21 -07001415class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001416 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001417 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001418 print "Loading target..."
1419 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1420 print "Loading source..."
1421 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1422
1423 self.verbatim_targets = verbatim_targets = []
1424 self.patch_list = patch_list = []
1425 diffs = []
1426 self.renames = renames = {}
1427 known_paths = set()
1428 largest_source_size = 0
1429
1430 matching_file_cache = {}
1431 for fn, sf in source_data.items():
1432 assert fn == sf.name
1433 matching_file_cache["path:" + fn] = sf
1434 if fn in target_data.keys():
1435 AddToKnownPaths(fn, known_paths)
1436 # Only allow eligibility for filename/sha matching
1437 # if there isn't a perfect path match.
1438 if target_data.get(sf.name) is None:
1439 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1440 matching_file_cache["sha:" + sf.sha1] = sf
1441
1442 for fn in sorted(target_data.keys()):
1443 tf = target_data[fn]
1444 assert fn == tf.name
1445 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1446 if sf is not None and sf.name != tf.name:
1447 print "File has moved from " + sf.name + " to " + tf.name
1448 renames[sf.name] = tf
1449
1450 if sf is None or fn in OPTIONS.require_verbatim:
1451 # This file should be included verbatim
1452 if fn in OPTIONS.prohibit_verbatim:
1453 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1454 print "send", fn, "verbatim"
1455 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001456 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001457 if fn in target_data.keys():
1458 AddToKnownPaths(fn, known_paths)
1459 elif tf.sha1 != sf.sha1:
1460 # File is different; consider sending as a patch
1461 diffs.append(common.Difference(tf, sf))
1462 else:
1463 # Target file data identical to source (may still be renamed)
1464 pass
1465
1466 common.ComputeDifferences(diffs)
1467
1468 for diff in diffs:
1469 tf, sf, d = diff.GetPatch()
1470 path = "/".join(tf.name.split("/")[:-1])
1471 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1472 path not in known_paths:
1473 # patch is almost as big as the file; don't bother patching
1474 # or a patch + rename cannot take place due to the target
1475 # directory not existing
1476 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001477 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001478 if sf.name in renames:
1479 del renames[sf.name]
1480 AddToKnownPaths(tf.name, known_paths)
1481 else:
1482 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1483 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1484 largest_source_size = max(largest_source_size, sf.size)
1485
1486 self.largest_source_size = largest_source_size
1487
1488 def EmitVerification(self, script):
1489 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001490 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001491 if tf.name != sf.name:
1492 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1493 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1494 so_far += sf.size
1495 return so_far
1496
Michael Runge63f01de2014-10-28 19:24:19 -07001497 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001498 for fn, _, sha1 in self.verbatim_targets:
1499 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001500 script.FileCheck("/"+fn, sha1)
1501 for tf, _, _, _ in self.patch_list:
1502 script.FileCheck(tf.name, tf.sha1)
1503
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001504 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001505 file_list = ["/" + i[0] for i in self.verbatim_targets]
1506 file_list += ["/" + i for i in self.source_data
1507 if i not in self.target_data and i not in self.renames]
1508 file_list += list(extras)
1509 # Sort the list in descending order, which removes all the files first
1510 # before attempting to remove the folder. (Bug: 22960996)
1511 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001512
1513 def TotalPatchSize(self):
1514 return sum(i[1].size for i in self.patch_list)
1515
1516 def EmitPatches(self, script, total_patch_size, so_far):
1517 self.deferred_patch_list = deferred_patch_list = []
1518 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001519 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001520 if tf.name == "system/build.prop":
1521 deferred_patch_list.append(item)
1522 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001523 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001524 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001525 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1526 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001527 so_far += tf.size
1528 script.SetProgress(so_far / total_patch_size)
1529 return so_far
1530
1531 def EmitDeferredPatches(self, script):
1532 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001533 tf, sf, _, _ = item
1534 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1535 "patch/" + sf.name + ".p")
1536 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001537
1538 def EmitRenames(self, script):
1539 if len(self.renames) > 0:
1540 script.Print("Renaming files...")
1541 for src, tgt in self.renames.iteritems():
1542 print "Renaming " + src + " to " + tgt.name
1543 script.RenameFile(src, tgt.name)
1544
1545
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001546def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001547 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1548 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1549
Doug Zongker26e66192014-02-20 13:22:07 -08001550 if (OPTIONS.block_based and
1551 target_has_recovery_patch and
1552 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001553 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1554
Doug Zongker37974732010-09-16 17:44:38 -07001555 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1556 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001557
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001558 if source_version == 0:
1559 print ("WARNING: generating edify script for a source that "
1560 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001561 script = edify_generator.EdifyGenerator(
1562 source_version, OPTIONS.target_info_dict,
1563 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001564
Michael Runge6e836112014-04-15 17:40:21 -07001565 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001566 recovery_mount_options = OPTIONS.source_info_dict.get(
1567 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001568 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001569 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001570 if OPTIONS.oem_source is None:
1571 raise common.ExternalError("OEM source required for this build")
Tao Baobd25fcd2016-03-07 21:24:40 -08001572 if not OPTIONS.oem_no_mount:
1573 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001574 oem_dict = common.LoadDictionaryFromLines(
1575 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001576
Dan Albert8b72aef2015-03-23 19:13:21 -07001577 metadata = {
1578 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1579 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001580 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001581 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001582
Tao Bao99c17252017-02-07 11:21:17 -08001583 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -08001584
Doug Zongker05d3dea2009-06-22 11:32:31 -07001585 device_specific = common.DeviceSpecificParams(
1586 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001587 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001588 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001589 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001590 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001591 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001592 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001593 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001594
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001595 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001596 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001597 if HasVendorPartition(target_zip):
1598 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001599 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001600 else:
1601 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001602
Dan Albert8b72aef2015-03-23 19:13:21 -07001603 target_fp = CalculateFingerprint(oem_props, oem_dict,
1604 OPTIONS.target_info_dict)
1605 source_fp = CalculateFingerprint(oem_props, oem_dict,
1606 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001607
1608 if oem_props is None:
1609 script.AssertSomeFingerprint(source_fp, target_fp)
1610 else:
1611 script.AssertSomeThumbprint(
1612 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1613 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1614
Doug Zongker2ea21062010-04-28 16:05:21 -07001615 metadata["pre-build"] = source_fp
1616 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001617 metadata["pre-build-incremental"] = GetBuildProp(
1618 "ro.build.version.incremental", OPTIONS.source_info_dict)
1619 metadata["post-build-incremental"] = GetBuildProp(
1620 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001621
Doug Zongker55d93282011-01-25 17:03:34 -08001622 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001623 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1624 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001625 target_boot = common.GetBootableImage(
1626 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001627 updating_boot = (not OPTIONS.two_step and
1628 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001629
Doug Zongker55d93282011-01-25 17:03:34 -08001630 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001631 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1632 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001633 target_recovery = common.GetBootableImage(
1634 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001635 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001636
Doug Zongker881dd402009-09-20 14:03:55 -07001637 # Here's how we divide up the progress bar:
1638 # 0.1 for verifying the start state (PatchCheck calls)
1639 # 0.8 for applying patches (ApplyPatch calls)
1640 # 0.1 for unpacking verbatim files, symlinking, and doing the
1641 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001642
Michael Runge6e836112014-04-15 17:40:21 -07001643 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001644 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001645
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001646 # Two-step incremental package strategy (in chronological order,
1647 # which is *not* the order in which the generated script has
1648 # things):
1649 #
1650 # if stage is not "2/3" or "3/3":
1651 # do verification on current system
1652 # write recovery image to boot partition
1653 # set stage to "2/3"
1654 # reboot to boot partition and restart recovery
1655 # else if stage is "2/3":
1656 # write recovery image to recovery partition
1657 # set stage to "3/3"
1658 # reboot to recovery partition and restart recovery
1659 # else:
1660 # (stage must be "3/3")
1661 # perform update:
1662 # patch system files, etc.
1663 # force full install of new boot image
1664 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001665 # complete script normally
1666 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001667
1668 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001669 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001670 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001671 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001672 assert fs.fs_type.upper() == "EMMC", \
1673 "two-step packages only supported on devices with EMMC /misc partitions"
1674 bcb_dev = {"bcb_dev": fs.device}
1675 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1676 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001677if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001678""" % bcb_dev)
Tao Bao47ec5ab2016-11-30 12:11:57 -08001679
1680 # Stage 2/3: Write recovery image to /recovery (currently running /boot).
1681 script.Comment("Stage 2/3")
Dan Albert8b72aef2015-03-23 19:13:21 -07001682 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001683 script.WriteRawImage("/recovery", "recovery.img")
1684 script.AppendExtra("""
1685set_stage("%(bcb_dev)s", "3/3");
1686reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001687else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001688""" % bcb_dev)
1689
Tao Bao47ec5ab2016-11-30 12:11:57 -08001690 # Stage 1/3: (a) Verify the current system.
1691 script.Comment("Stage 1/3")
1692
Tao Bao6c55a8a2015-04-08 15:30:27 -07001693 # Dump fingerprints
1694 script.Print("Source: %s" % (source_fp,))
1695 script.Print("Target: %s" % (target_fp,))
1696
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001697 script.Print("Verifying current system...")
1698
Doug Zongkere5ff5902012-01-17 10:55:37 -08001699 device_specific.IncrementalOTA_VerifyBegin()
1700
Doug Zongker881dd402009-09-20 14:03:55 -07001701 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001702 so_far = system_diff.EmitVerification(script)
1703 if vendor_diff:
1704 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001705
Tao Baob4cfca52016-02-04 14:26:02 -08001706 size = []
1707 if system_diff.patch_list:
1708 size.append(system_diff.largest_source_size)
1709 if vendor_diff:
1710 if vendor_diff.patch_list:
1711 size.append(vendor_diff.largest_source_size)
1712
Doug Zongker5da317e2009-06-02 13:38:17 -07001713 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001714 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001715 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001716 print "boot target: %d source: %d diff: %d" % (
1717 target_boot.size, source_boot.size, len(d))
1718
Doug Zongker048e7ca2009-06-15 14:31:53 -07001719 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001720
Tao Baodd24da92015-07-29 14:09:23 -07001721 boot_type, boot_device = common.GetTypeAndDevice(
1722 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001723
1724 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1725 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001726 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001727 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001728 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001729 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001730
Tao Baob4cfca52016-02-04 14:26:02 -08001731 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001732 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001733
Doug Zongker05d3dea2009-06-22 11:32:31 -07001734 device_specific.IncrementalOTA_VerifyEnd()
1735
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001736 if OPTIONS.two_step:
Tao Bao47ec5ab2016-11-30 12:11:57 -08001737 # Stage 1/3: (b) Write recovery image to /boot.
1738 _WriteRecoveryImageToBoot(script, output_zip)
1739
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001740 script.AppendExtra("""
1741set_stage("%(bcb_dev)s", "2/3");
1742reboot_now("%(bcb_dev)s", "");
1743else
1744""" % bcb_dev)
1745
Tao Bao47ec5ab2016-11-30 12:11:57 -08001746 # Stage 3/3: Make changes.
1747 script.Comment("Stage 3/3")
1748
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001749 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001750
Doug Zongkere5ff5902012-01-17 10:55:37 -08001751 device_specific.IncrementalOTA_InstallBegin()
1752
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001753 if OPTIONS.two_step:
1754 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1755 script.WriteRawImage("/boot", "boot.img")
1756 print "writing full boot image (forced by two-step mode)"
1757
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001758 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001759 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1760 if vendor_diff:
1761 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001762
Doug Zongker881dd402009-09-20 14:03:55 -07001763 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001764 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1765 if vendor_diff:
1766 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001767 if updating_boot:
1768 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001769
1770 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001771 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1772 if vendor_diff:
1773 script.Print("Patching vendor files...")
1774 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001775
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001776 if not OPTIONS.two_step:
1777 if updating_boot:
1778 # Produce the boot image by applying a patch to the current
1779 # contents of the boot partition, and write it back to the
1780 # partition.
1781 script.Print("Patching boot image...")
1782 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1783 % (boot_type, boot_device,
1784 source_boot.size, source_boot.sha1,
1785 target_boot.size, target_boot.sha1),
1786 "-",
1787 target_boot.size, target_boot.sha1,
1788 source_boot.sha1, "patch/boot.img.p")
1789 so_far += target_boot.size
1790 script.SetProgress(so_far / total_patch_size)
1791 print "boot image changed; including."
1792 else:
1793 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001794
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001795 system_items = ItemSet("system", "META/filesystem_config.txt")
1796 if vendor_diff:
1797 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1798
Doug Zongkereef39442009-04-02 12:14:19 -07001799 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001800 # Recovery is generated as a patch using both the boot image
1801 # (which contains the same linux kernel as recovery) and the file
1802 # /system/etc/recovery-resource.dat (which contains all the images
1803 # used in the recovery UI) as sources. This lets us minimize the
1804 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001805 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001806 # For older builds where recovery-resource.dat is not present, we
1807 # use only the boot image as the source.
1808
Doug Zongkerc9253822014-02-04 12:17:58 -08001809 if not target_has_recovery_patch:
1810 def output_sink(fn, data):
1811 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001812 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001813
1814 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1815 target_recovery, target_boot)
1816 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001817 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001818 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001819 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001820 else:
1821 print "recovery image unchanged; skipping."
1822
Doug Zongker881dd402009-09-20 14:03:55 -07001823 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001824
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001825 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1826 if vendor_diff:
1827 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1828
1829 temp_script = script.MakeTemporary()
1830 system_items.GetMetadata(target_zip)
1831 system_items.Get("system").SetPermissions(temp_script)
1832 if vendor_diff:
1833 vendor_items.GetMetadata(target_zip)
1834 vendor_items.Get("vendor").SetPermissions(temp_script)
1835
1836 # Note that this call will mess up the trees of Items, so make sure
1837 # we're done with them.
1838 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1839 if vendor_diff:
1840 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001841
1842 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001843 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1844
1845 # Delete all the symlinks in source that aren't in target. This
1846 # needs to happen before verbatim files are unpacked, in case a
1847 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001848
1849 # If a symlink in the source will be replaced by a regular file, we cannot
1850 # delete the symlink/file in case the package gets applied again. For such
1851 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1852 # (Bug: 23646151)
1853 replaced_symlinks = dict()
1854 if system_diff:
1855 for i in system_diff.verbatim_targets:
1856 replaced_symlinks["/%s" % (i[0],)] = i[2]
1857 if vendor_diff:
1858 for i in vendor_diff.verbatim_targets:
1859 replaced_symlinks["/%s" % (i[0],)] = i[2]
1860
1861 if system_diff:
1862 for tf in system_diff.renames.values():
1863 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1864 if vendor_diff:
1865 for tf in vendor_diff.renames.values():
1866 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1867
1868 always_delete = []
1869 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001870 for dest, link in source_symlinks:
1871 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001872 if link in replaced_symlinks:
1873 may_delete.append((link, replaced_symlinks[link]))
1874 else:
1875 always_delete.append(link)
1876 script.DeleteFiles(always_delete)
1877 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001878
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001879 if system_diff.verbatim_targets:
1880 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001881 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001882 if vendor_diff and vendor_diff.verbatim_targets:
1883 script.Print("Unpacking new vendor files...")
1884 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001885
Doug Zongkerc9253822014-02-04 12:17:58 -08001886 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001887 script.Print("Unpacking new recovery...")
1888 script.UnpackPackageDir("recovery", "/system")
1889
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001890 system_diff.EmitRenames(script)
1891 if vendor_diff:
1892 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001893
Doug Zongker05d3dea2009-06-22 11:32:31 -07001894 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001895
1896 # Create all the symlinks that don't already exist, or point to
1897 # somewhere different than what we want. Delete each symlink before
1898 # creating it, since the 'symlink' command won't overwrite.
1899 to_create = []
1900 for dest, link in target_symlinks:
1901 if link in source_symlinks_d:
1902 if dest != source_symlinks_d[link]:
1903 to_create.append((dest, link))
1904 else:
1905 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001906 script.DeleteFiles([i[1] for i in to_create])
1907 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001908
1909 # Now that the symlinks are created, we can set all the
1910 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001911 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001912
Doug Zongker881dd402009-09-20 14:03:55 -07001913 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001914 device_specific.IncrementalOTA_InstallEnd()
1915
Doug Zongker1c390a22009-05-14 19:06:36 -07001916 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001917 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001918
Doug Zongkere92f15a2011-08-26 13:46:40 -07001919 # Patch the build.prop file last, so if something fails but the
1920 # device can still come up, it appears to be the old build and will
1921 # get set the OTA package again to retry.
1922 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001923 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001924
Doug Zongker922206e2014-03-04 13:16:24 -08001925 if OPTIONS.wipe_user_data:
1926 script.Print("Erasing user data...")
1927 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001928 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001929
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001930 if OPTIONS.two_step:
1931 script.AppendExtra("""
1932set_stage("%(bcb_dev)s", "");
1933endif;
1934endif;
1935""" % bcb_dev)
1936
Michael Runge63f01de2014-10-28 19:24:19 -07001937 if OPTIONS.verify and system_diff:
1938 script.Print("Remounting and verifying system partition files...")
1939 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001940 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001941 system_diff.EmitExplicitTargetVerification(script)
1942
1943 if OPTIONS.verify and vendor_diff:
1944 script.Print("Remounting and verifying vendor partition files...")
1945 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001946 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001947 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001948
1949 # For downgrade OTAs, we prefer to use the update-binary in the source
1950 # build that is actually newer than the one in the target build.
1951 if OPTIONS.downgrade:
1952 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1953 else:
1954 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001955
Tao Baob4cfca52016-02-04 14:26:02 -08001956 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001957 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001958
1959
1960def main(argv):
1961
1962 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001963 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001964 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001965 elif o in ("-k", "--package_key"):
1966 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001967 elif o in ("-i", "--incremental_from"):
1968 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001969 elif o == "--full_radio":
1970 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001971 elif o == "--full_bootloader":
1972 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001973 elif o in ("-w", "--wipe_user_data"):
1974 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001975 elif o in ("-n", "--no_prereq"):
1976 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08001977 elif o == "--downgrade":
1978 OPTIONS.downgrade = True
1979 OPTIONS.wipe_user_data = True
Tao Bao9f884e62017-02-28 11:48:48 -08001980 elif o == "--override_timestamp":
1981 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001982 elif o in ("-o", "--oem_settings"):
1983 OPTIONS.oem_source = a
Tao Baodf4cb0b2016-02-25 19:49:55 -08001984 elif o == "--oem_no_mount":
1985 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001986 elif o in ("-e", "--extra_script"):
1987 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001988 elif o in ("-a", "--aslr_mode"):
1989 if a in ("on", "On", "true", "True", "yes", "Yes"):
1990 OPTIONS.aslr_mode = True
1991 else:
1992 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001993 elif o in ("-t", "--worker_threads"):
1994 if a.isdigit():
1995 OPTIONS.worker_threads = int(a)
1996 else:
1997 raise ValueError("Cannot parse value %r for option %r - only "
1998 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001999 elif o in ("-2", "--two_step"):
2000 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08002001 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002002 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07002003 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07002004 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08002005 elif o == "--block":
2006 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08002007 elif o in ("-b", "--binary"):
2008 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07002009 elif o in ("--no_fallback_to_full",):
2010 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07002011 elif o == "--stash_threshold":
2012 try:
2013 OPTIONS.stash_threshold = float(a)
2014 except ValueError:
2015 raise ValueError("Cannot parse value %r for option %r - expecting "
2016 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08002017 elif o == "--gen_verify":
2018 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08002019 elif o == "--log_diff":
2020 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07002021 elif o == "--payload_signer":
2022 OPTIONS.payload_signer = a
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002023 elif o == "--payload_signer_args":
2024 OPTIONS.payload_signer_args = shlex.split(a)
Doug Zongkereef39442009-04-02 12:14:19 -07002025 else:
2026 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07002027 return True
Doug Zongkereef39442009-04-02 12:14:19 -07002028
2029 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07002030 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07002031 extra_long_opts=[
2032 "board_config=",
2033 "package_key=",
2034 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07002035 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07002036 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07002037 "wipe_user_data",
2038 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08002039 "downgrade",
Tao Bao9f884e62017-02-28 11:48:48 -08002040 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07002041 "extra_script=",
2042 "worker_threads=",
2043 "aslr_mode=",
2044 "two_step",
2045 "no_signing",
2046 "block",
2047 "binary=",
2048 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08002049 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07002050 "verify",
2051 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07002052 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08002053 "gen_verify",
2054 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07002055 "payload_signer=",
Baligh Uddin2abbbd02016-06-22 12:14:16 -07002056 "payload_signer_args=",
Dan Albert8b72aef2015-03-23 19:13:21 -07002057 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07002058
2059 if len(args) != 2:
2060 common.Usage(__doc__)
2061 sys.exit(1)
2062
Tao Bao4da324e2016-02-23 11:38:39 -08002063 if OPTIONS.downgrade:
2064 # Sanity check to enforce a data wipe.
2065 if not OPTIONS.wipe_user_data:
2066 raise ValueError("Cannot downgrade without a data wipe")
2067
2068 # We should only allow downgrading incrementals (as opposed to full).
2069 # Otherwise the device may go back from arbitrary build with this full
2070 # OTA package.
2071 if OPTIONS.incremental_source is None:
2072 raise ValueError("Cannot generate downgradable full OTAs - consider"
2073 "using --omit_prereq?")
2074
Tao Bao9f884e62017-02-28 11:48:48 -08002075 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
2076 "Cannot have --downgrade AND --override_timestamp both"
2077
Tao Baoc098e9e2016-01-07 13:03:56 -08002078 # Load the dict file from the zip directly to have a peek at the OTA type.
2079 # For packages using A/B update, unzipping is not needed.
2080 input_zip = zipfile.ZipFile(args[0], "r")
2081 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
2082 common.ZipClose(input_zip)
2083
2084 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
2085
2086 if ab_update:
2087 if OPTIONS.incremental_source is not None:
2088 OPTIONS.target_info_dict = OPTIONS.info_dict
2089 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
2090 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
2091 common.ZipClose(source_zip)
2092
2093 if OPTIONS.verbose:
2094 print "--- target info ---"
2095 common.DumpInfoDict(OPTIONS.info_dict)
2096
2097 if OPTIONS.incremental_source is not None:
2098 print "--- source info ---"
2099 common.DumpInfoDict(OPTIONS.source_info_dict)
2100
2101 WriteABOTAPackageWithBrilloScript(
2102 target_file=args[0],
2103 output_file=args[1],
2104 source_file=OPTIONS.incremental_source)
2105
2106 print "done."
2107 return
2108
Doug Zongker1c390a22009-05-14 19:06:36 -07002109 if OPTIONS.extra_script is not None:
2110 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2111
Doug Zongkereef39442009-04-02 12:14:19 -07002112 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002113 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002114
Doug Zongkereef39442009-04-02 12:14:19 -07002115 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002116 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002117
Doug Zongker37974732010-09-16 17:44:38 -07002118 if OPTIONS.verbose:
2119 print "--- target info ---"
2120 common.DumpInfoDict(OPTIONS.info_dict)
2121
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002122 # If the caller explicitly specified the device-specific extensions
2123 # path via -s/--device_specific, use that. Otherwise, use
2124 # META/releasetools.py if it is present in the target target_files.
2125 # Otherwise, take the path of the file from 'tool_extensions' in the
2126 # info dict and look for that in the local filesystem, relative to
2127 # the current directory.
2128
Doug Zongker37974732010-09-16 17:44:38 -07002129 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002130 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2131 if os.path.exists(from_input):
2132 print "(using device-specific extensions from target_files)"
2133 OPTIONS.device_specific = from_input
2134 else:
2135 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2136
Doug Zongker37974732010-09-16 17:44:38 -07002137 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002138 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002139
Tao Baoc098e9e2016-01-07 13:03:56 -08002140 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002141 raise common.ExternalError(
2142 "--- target build has specified no recovery ---")
2143
Tao Bao767e3ac2015-11-10 12:19:19 -08002144 # Use the default key to sign the package if not specified with package_key.
2145 if not OPTIONS.no_signing:
2146 if OPTIONS.package_key is None:
2147 OPTIONS.package_key = OPTIONS.info_dict.get(
2148 "default_system_dev_certificate",
2149 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002150
Tao Bao767e3ac2015-11-10 12:19:19 -08002151 # Set up the output zip. Create a temporary zip file if signing is needed.
2152 if OPTIONS.no_signing:
2153 if os.path.exists(args[1]):
2154 os.unlink(args[1])
2155 output_zip = zipfile.ZipFile(args[1], "w",
2156 compression=zipfile.ZIP_DEFLATED)
2157 else:
2158 temp_zip_file = tempfile.NamedTemporaryFile()
2159 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2160 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002161
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002162 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002163 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002164 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002165 print "--- can't determine the cache partition size ---"
2166 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002167
Tao Bao9bc6bb22015-11-09 16:58:28 -08002168 # Generate a verify package.
2169 if OPTIONS.gen_verify:
2170 WriteVerifyPackage(input_zip, output_zip)
2171
Tao Bao767e3ac2015-11-10 12:19:19 -08002172 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002173 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002174 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002175
2176 # Generate an incremental OTA. It will fall back to generate a full OTA on
2177 # failure unless no_fallback_to_full is specified.
2178 else:
2179 print "unzipping source target-files..."
2180 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2181 OPTIONS.incremental_source)
2182 OPTIONS.target_info_dict = OPTIONS.info_dict
2183 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2184 OPTIONS.source_tmp)
2185 if OPTIONS.verbose:
2186 print "--- source info ---"
2187 common.DumpInfoDict(OPTIONS.source_info_dict)
2188 try:
2189 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002190 if OPTIONS.log_diff:
2191 out_file = open(OPTIONS.log_diff, 'w')
2192 import target_files_diff
2193 target_files_diff.recursiveDiff('',
2194 OPTIONS.source_tmp,
2195 OPTIONS.input_tmp,
2196 out_file)
2197 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002198 except ValueError:
2199 if not OPTIONS.fallback_to_full:
2200 raise
2201 print "--- failed to build incremental; falling back to full ---"
2202 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002203 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002204
Tao Bao767e3ac2015-11-10 12:19:19 -08002205 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002206
Tao Bao767e3ac2015-11-10 12:19:19 -08002207 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002208 if not OPTIONS.no_signing:
2209 SignOutput(temp_zip_file.name, args[1])
2210 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002211
Doug Zongkereef39442009-04-02 12:14:19 -07002212 print "done."
2213
2214
2215if __name__ == '__main__':
2216 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002217 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002218 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002219 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002220 print
2221 print " ERROR: %s" % (e,)
2222 print
2223 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002224 finally:
2225 common.Cleanup()