blob: 27adb94e35a90d10fb9728d60f0ff146319b91c5 [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.
Doug Zongkereef39442009-04-02 12:14:19 -0700128"""
129
130import sys
131
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800132if sys.hexversion < 0x02070000:
133 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700134 sys.exit(1)
135
Doug Zongkerfc44a512014-08-26 13:10:25 -0700136import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700137import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800138import subprocess
Doug Zongkereef39442009-04-02 12:14:19 -0700139import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700140import zipfile
141
142import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700143import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700144import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700145
146OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700147OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700148OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700149OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700150OPTIONS.require_verbatim = set()
151OPTIONS.prohibit_verbatim = set(("system/build.prop",))
152OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700153OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700154OPTIONS.omit_prereq = False
Tao Bao4da324e2016-02-23 11:38:39 -0800155OPTIONS.downgrade = False
Tao Bao9f884e62017-02-28 11:48:48 -0800156OPTIONS.timestamp = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700157OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700158OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700159OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
160if OPTIONS.worker_threads == 0:
161 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800162OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900163OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800164OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800165OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700166OPTIONS.oem_source = None
Tao Baodf4cb0b2016-02-25 19:49:55 -0800167OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700168OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700169OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700170OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700171# Stash size cannot exceed cache_size * threshold.
172OPTIONS.cache_size = None
173OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800174OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800175OPTIONS.log_diff = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700176
Doug Zongkereef39442009-04-02 12:14:19 -0700177def MostPopularKey(d, default):
178 """Given a dict, return the key corresponding to the largest
179 value. Returns 'default' if the dict is empty."""
180 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700181 if not x:
182 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700183 x.sort()
184 return x[-1][1]
185
186
187def IsSymlink(info):
188 """Return true if the zipfile.ZipInfo object passed in represents a
189 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700190 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700191
Hristo Bojinov96be7202010-08-02 10:26:17 -0700192def IsRegular(info):
193 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700194 regular file."""
195 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700196
Michael Runge4038aa82013-12-13 18:06:28 -0800197def ClosestFileMatch(src, tgtfiles, existing):
198 """Returns the closest file match between a source file and list
199 of potential matches. The exact filename match is preferred,
200 then the sha1 is searched for, and finally a file with the same
201 basename is evaluated. Rename support in the updater-binary is
202 required for the latter checks to be used."""
203
204 result = tgtfiles.get("path:" + src.name)
205 if result is not None:
206 return result
207
208 if not OPTIONS.target_info_dict.get("update_rename_support", False):
209 return None
210
211 if src.size < 1000:
212 return None
213
214 result = tgtfiles.get("sha1:" + src.sha1)
215 if result is not None and existing.get(result.name) is None:
216 return result
217 result = tgtfiles.get("file:" + src.name.split("/")[-1])
218 if result is not None and existing.get(result.name) is None:
219 return result
220 return None
221
Dan Albert8b72aef2015-03-23 19:13:21 -0700222class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700223 def __init__(self, partition, fs_config):
224 self.partition = partition
225 self.fs_config = fs_config
226 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700227
Dan Albert8b72aef2015-03-23 19:13:21 -0700228 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700229 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700230 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700231 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700232
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700233 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700234 # The target_files contains a record of what the uid,
235 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700236 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700237
238 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700239 if not line:
240 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700241 columns = line.split()
242 name, uid, gid, mode = columns[:4]
243 selabel = None
244 capabilities = None
245
246 # After the first 4 columns, there are a series of key=value
247 # pairs. Extract out the fields we care about.
248 for element in columns[4:]:
249 key, value = element.split("=")
250 if key == "selabel":
251 selabel = value
252 if key == "capabilities":
253 capabilities = value
254
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700255 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700256 if i is not None:
257 i.uid = int(uid)
258 i.gid = int(gid)
259 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700260 i.selabel = selabel
261 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700262 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700263 i.children.sort(key=lambda i: i.name)
264
Tao Baof2cffbd2015-07-22 12:33:18 -0700265 # Set metadata for the files generated by this script. For full recovery
266 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700267 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700268 if i:
269 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700270 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700271 if i:
272 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700273
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700274
Dan Albert8b72aef2015-03-23 19:13:21 -0700275class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700276 """Items represent the metadata (user, group, mode) of files and
277 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700278 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700279 self.itemset = itemset
280 self.name = name
281 self.uid = None
282 self.gid = None
283 self.mode = None
284 self.selabel = None
285 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700286 self.is_dir = is_dir
287 self.descendants = None
288 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700289
290 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700291 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700292 self.parent.children.append(self)
293 else:
294 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700295 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700296 self.children = []
297
298 def Dump(self, indent=0):
299 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700300 print "%s%s %d %d %o" % (
301 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700302 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700303 print "%s%s %s %s %s" % (
304 " " * indent, self.name, self.uid, self.gid, self.mode)
305 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700306 print "%s%s" % (" "*indent, self.descendants)
307 print "%s%s" % (" "*indent, self.best_subtree)
308 for i in self.children:
309 i.Dump(indent=indent+1)
310
Doug Zongkereef39442009-04-02 12:14:19 -0700311 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700312 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700313 all children and determine the best strategy for using set_perm_recursive
314 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700315 values. Recursively calls itself for all descendants.
316
Dan Albert8b72aef2015-03-23 19:13:21 -0700317 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
318 counting up all descendants of this node. (dmode or fmode may be None.)
319 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
320 fmode, selabel, capabilities) tuple that will match the most descendants of
321 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700322 """
323
Dan Albert8b72aef2015-03-23 19:13:21 -0700324 assert self.is_dir
325 key = (self.uid, self.gid, self.mode, None, self.selabel,
326 self.capabilities)
327 self.descendants = {key: 1}
328 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700329 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700330 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700331 for k, v in i.CountChildMetadata().iteritems():
332 d[k] = d.get(k, 0) + v
333 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700334 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700335 d[k] = d.get(k, 0) + 1
336
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700337 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
338 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700339
340 # First, find the (uid, gid) pair that matches the most
341 # descendants.
342 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700343 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700344 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
345 ug = MostPopularKey(ug, (0, 0))
346
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700347 # Now find the dmode, fmode, selabel, and capabilities that match
348 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700349 best_dmode = (0, 0o755)
350 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700351 best_selabel = (0, None)
352 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700353 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700354 if k[:2] != ug:
355 continue
356 if k[2] is not None and count >= best_dmode[0]:
357 best_dmode = (count, k[2])
358 if k[3] is not None and count >= best_fmode[0]:
359 best_fmode = (count, k[3])
360 if k[4] is not None and count >= best_selabel[0]:
361 best_selabel = (count, k[4])
362 if k[5] is not None and count >= best_capabilities[0]:
363 best_capabilities = (count, k[5])
364 self.best_subtree = ug + (
365 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700366
367 return d
368
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700369 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700370 """Append set_perm/set_perm_recursive commands to 'script' to
371 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700372 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700373
374 self.CountChildMetadata()
375
376 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700377 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
378 # that the current item (and all its children) have already been set to.
379 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700380 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700381 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700382 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700383 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700384 current = item.best_subtree
385
386 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700387 item.mode != current[2] or item.selabel != current[4] or \
388 item.capabilities != current[5]:
389 script.SetPermissions("/"+item.name, item.uid, item.gid,
390 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700391
392 for i in item.children:
393 recurse(i, current)
394 else:
395 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700396 item.mode != current[3] or item.selabel != current[4] or \
397 item.capabilities != current[5]:
398 script.SetPermissions("/"+item.name, item.uid, item.gid,
399 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700400
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700401 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700402
403
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700404def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
405 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700406 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800407 list of symlinks. output_zip may be None, in which case the copy is
408 skipped (but the other side effects still happen). substitute is an
409 optional dict of {output filename: contents} to be output instead of
410 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700411 """
412
413 symlinks = []
414
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700415 partition = itemset.partition
416
Doug Zongkereef39442009-04-02 12:14:19 -0700417 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700418 prefix = partition.upper() + "/"
419 if info.filename.startswith(prefix):
420 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700421 if IsSymlink(info):
422 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700423 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700424 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700425 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700426 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700427 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700428 if substitute and fn in substitute and substitute[fn] is None:
429 continue
430 if output_zip is not None:
431 if substitute and fn in substitute:
432 data = substitute[fn]
433 else:
434 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700435 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700436 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700437 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700438 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700439 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700440
441 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800442 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700443
444
Doug Zongkereef39442009-04-02 12:14:19 -0700445def SignOutput(temp_zip_name, output_zip_name):
446 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
447 pw = key_passwords[OPTIONS.package_key]
448
Doug Zongker951495f2009-08-14 12:44:19 -0700449 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
450 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700451
452
Dan Albert8b72aef2015-03-23 19:13:21 -0700453def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700454 oem_props = info_dict.get("oem_fingerprint_properties")
Michael Runge560569a2014-09-18 15:12:45 -0700455 if oem_props is None or len(oem_props) == 0:
Michael Runge6e836112014-04-15 17:40:21 -0700456 device = GetBuildProp("ro.product.device", info_dict)
457 script.AssertDevice(device)
458 else:
459 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700460 raise common.ExternalError(
461 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700462 for prop in oem_props.split():
463 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700464 raise common.ExternalError(
465 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700466 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700467
Doug Zongkereef39442009-04-02 12:14:19 -0700468
Doug Zongkerc9253822014-02-04 12:17:58 -0800469def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700470 namelist = [name for name in target_files_zip.namelist()]
471 return ("SYSTEM/recovery-from-boot.p" in namelist or
472 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700473
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700474def HasVendorPartition(target_files_zip):
475 try:
476 target_files_zip.getinfo("VENDOR/")
477 return True
478 except KeyError:
479 return False
480
Michael Runge6e836112014-04-15 17:40:21 -0700481def GetOemProperty(name, oem_props, oem_dict, info_dict):
482 if oem_props is not None and name in oem_props:
483 return oem_dict[name]
484 return GetBuildProp(name, info_dict)
485
486
487def CalculateFingerprint(oem_props, oem_dict, info_dict):
488 if oem_props is None:
489 return GetBuildProp("ro.build.fingerprint", info_dict)
490 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700491 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
492 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
493 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
494 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700495
Doug Zongkerfc44a512014-08-26 13:10:25 -0700496
Doug Zongker3c84f562014-07-31 11:06:30 -0700497def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700498 # Return an image object (suitable for passing to BlockImageDiff)
499 # for the 'which' partition (most be "system" or "vendor"). If a
500 # prebuilt image and file map are found in tmpdir they are used,
501 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700502
503 assert which in ("system", "vendor")
504
505 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700506 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
507 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700508 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700509 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700510
511 else:
512 print "building %s.img from target-files" % (which,)
513
514 # This is an 'old' target-files, which does not contain images
515 # already built. Build them.
516
Doug Zongkerfc44a512014-08-26 13:10:25 -0700517 mappath = tempfile.mkstemp()[1]
518 OPTIONS.tempfiles.append(mappath)
519
Doug Zongker3c84f562014-07-31 11:06:30 -0700520 import add_img_to_target_files
521 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700522 path = add_img_to_target_files.BuildSystem(
523 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700524 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700525 path = add_img_to_target_files.BuildVendor(
526 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700527
Tao Baoff777812015-05-12 11:42:31 -0700528 # Bug: http://b/20939131
529 # In ext4 filesystems, block 0 might be changed even being mounted
530 # R/O. We add it to clobbered_blocks so that it will be written to the
531 # target unconditionally. Note that they are still part of care_map.
532 clobbered_blocks = "0"
533
534 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700535
536
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700537def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700538 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700539 # be installed on top of. For now, we expect the API just won't
540 # change very often. Similarly for fstab, it might have changed
541 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700542 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700543
Michael Runge6e836112014-04-15 17:40:21 -0700544 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700545 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700546 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700547 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700548 if OPTIONS.oem_source is None:
549 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800550 if not OPTIONS.oem_no_mount:
551 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700552 oem_dict = common.LoadDictionaryFromLines(
553 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700554
Dan Albert8b72aef2015-03-23 19:13:21 -0700555 metadata = {
556 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700557 OPTIONS.info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700558 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
559 OPTIONS.info_dict),
560 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
561 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700562
Doug Zongker05d3dea2009-06-22 11:32:31 -0700563 device_specific = common.DeviceSpecificParams(
564 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700565 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700566 output_zip=output_zip,
567 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700568 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700569 metadata=metadata,
570 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700571
Doug Zongkerc9253822014-02-04 12:17:58 -0800572 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800573 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800574
Tao Baob4cfca52016-02-04 14:26:02 -0800575 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
576
Doug Zongker962069c2009-04-23 11:41:58 -0700577 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700578 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700579 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
580 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700581
Michael Runge6e836112014-04-15 17:40:21 -0700582 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700583 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800584
585 # Two-step package strategy (in chronological order, which is *not*
586 # the order in which the generated script has things):
587 #
588 # if stage is not "2/3" or "3/3":
589 # write recovery image to boot partition
590 # set stage to "2/3"
591 # reboot to boot partition and restart recovery
592 # else if stage is "2/3":
593 # write recovery image to recovery partition
594 # set stage to "3/3"
595 # reboot to recovery partition and restart recovery
596 # else:
597 # (stage must be "3/3")
598 # set stage to ""
599 # do normal full package installation:
600 # wipe and install system, boot image, etc.
601 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700602 # complete script normally
603 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800604
605 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
606 OPTIONS.input_tmp, "RECOVERY")
607 if OPTIONS.two_step:
608 if not OPTIONS.info_dict.get("multistage_support", None):
609 assert False, "two-step packages not supported by this build"
610 fs = OPTIONS.info_dict["fstab"]["/misc"]
611 assert fs.fs_type.upper() == "EMMC", \
612 "two-step packages only supported on devices with EMMC /misc partitions"
613 bcb_dev = {"bcb_dev": fs.device}
614 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
615 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700616if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800617""" % bcb_dev)
618 script.WriteRawImage("/recovery", "recovery.img")
619 script.AppendExtra("""
620set_stage("%(bcb_dev)s", "3/3");
621reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700622else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800623""" % bcb_dev)
624
Tao Bao6c55a8a2015-04-08 15:30:27 -0700625 # Dump fingerprints
626 script.Print("Target: %s" % CalculateFingerprint(
627 oem_props, oem_dict, OPTIONS.info_dict))
628
Doug Zongkere5ff5902012-01-17 10:55:37 -0800629 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700630
Doug Zongker01ce19c2014-02-04 13:48:15 -0800631 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700632
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700633 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800634 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700635 if HasVendorPartition(input_zip):
636 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700637
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400638 # Place a copy of file_contexts.bin into the OTA package which will be used
639 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700640 if "selinux_fc" in OPTIONS.info_dict:
641 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500642
Michael Runge7cd99ba2014-10-22 17:21:48 -0700643 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
644
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700645 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700646 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800647
Doug Zongker26e66192014-02-20 13:22:07 -0800648 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700649 # Full OTA is done as an "incremental" against an empty source
650 # image. This has the effect of writing new data from the package
651 # to the entire partition, but lets us reuse the updater code that
652 # writes incrementals to do it.
653 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
654 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700655 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700656 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800657 else:
658 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700659 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800660 if not has_recovery_patch:
661 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800662 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700663
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700664 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800665 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700666
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700667 boot_img = common.GetBootableImage(
668 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800669
Doug Zongker91a99c22014-05-09 13:15:01 -0700670 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800671 def output_sink(fn, data):
672 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700673 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800674
675 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
676 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700677
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700678 system_items.GetMetadata(input_zip)
679 system_items.Get("system").SetPermissions(script)
680
681 if HasVendorPartition(input_zip):
682 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
683 script.ShowProgress(0.1, 0)
684
685 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700686 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
687 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700688 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700689 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700690 else:
691 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700692 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700693 script.UnpackPackageDir("vendor", "/vendor")
694
695 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
696 script.MakeSymlinks(symlinks)
697
698 vendor_items.GetMetadata(input_zip)
699 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700700
Doug Zongker37974732010-09-16 17:44:38 -0700701 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700702 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700703
Doug Zongker01ce19c2014-02-04 13:48:15 -0800704 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700705 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700706
Doug Zongker01ce19c2014-02-04 13:48:15 -0800707 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700708 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700709
Doug Zongker1c390a22009-05-14 19:06:36 -0700710 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700711 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700712
Doug Zongker14833602010-02-02 13:12:04 -0800713 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800714
Doug Zongker922206e2014-03-04 13:16:24 -0800715 if OPTIONS.wipe_user_data:
716 script.ShowProgress(0.1, 10)
717 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700718
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800719 if OPTIONS.two_step:
720 script.AppendExtra("""
721set_stage("%(bcb_dev)s", "");
722""" % bcb_dev)
723 script.AppendExtra("else\n")
724 script.WriteRawImage("/boot", "recovery.img")
725 script.AppendExtra("""
726set_stage("%(bcb_dev)s", "2/3");
727reboot_now("%(bcb_dev)s", "");
728endif;
729endif;
730""" % bcb_dev)
Tao Baob4cfca52016-02-04 14:26:02 -0800731
Tao Bao4da324e2016-02-23 11:38:39 -0800732 script.SetProgress(1)
733 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -0800734 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700735 WriteMetadata(metadata, output_zip)
736
Doug Zongkerfc44a512014-08-26 13:10:25 -0700737
Dan Albert8e0178d2015-01-27 15:53:15 -0800738def WritePolicyConfig(file_name, output_zip):
739 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500740
Doug Zongker2ea21062010-04-28 16:05:21 -0700741
742def WriteMetadata(metadata, output_zip):
743 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
744 "".join(["%s=%s\n" % kv
745 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700746
Doug Zongkerfc44a512014-08-26 13:10:25 -0700747
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700748def LoadPartitionFiles(z, partition):
749 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700750 ZipFile, and return a dict of {filename: File object}."""
751 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700752 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700753 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700754 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700755 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700756 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700757 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700758 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800759 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700760
761
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700762def GetBuildProp(prop, info_dict):
763 """Return the fingerprint of the build of a given target-files info_dict."""
764 try:
765 return info_dict.get("build.prop", {})[prop]
766 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700767 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700768
Doug Zongkerfc44a512014-08-26 13:10:25 -0700769
Michael Runge4038aa82013-12-13 18:06:28 -0800770def AddToKnownPaths(filename, known_paths):
771 if filename[-1] == "/":
772 return
773 dirs = filename.split("/")[:-1]
774 while len(dirs) > 0:
775 path = "/".join(dirs)
776 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700777 break
Michael Runge4038aa82013-12-13 18:06:28 -0800778 known_paths.add(path)
779 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700780
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700781
Tao Bao99c17252017-02-07 11:21:17 -0800782def HandleDowngradeMetadata(metadata):
783 # Only incremental OTAs are allowed to reach here.
784 assert OPTIONS.incremental_source is not None
785
786 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
787 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
788 is_downgrade = long(post_timestamp) < long(pre_timestamp)
789
790 if OPTIONS.downgrade:
Tao Bao99c17252017-02-07 11:21:17 -0800791 if not is_downgrade:
792 raise RuntimeError("--downgrade specified but no downgrade detected: "
793 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
Tao Bao9f884e62017-02-28 11:48:48 -0800794 metadata["ota-downgrade"] = "yes"
795 elif OPTIONS.timestamp:
796 if not is_downgrade:
797 raise RuntimeError("--timestamp specified but no timestamp hack needed: "
798 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
799 metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
Tao Bao99c17252017-02-07 11:21:17 -0800800 else:
801 if is_downgrade:
Tao Bao9f884e62017-02-28 11:48:48 -0800802 raise RuntimeError("Downgrade detected based on timestamp check: "
803 "pre: %s, post: %s. Need to specify --timestamp OR "
804 "--downgrade to allow building the incremental." % (
805 pre_timestamp, post_timestamp))
Tao Bao99c17252017-02-07 11:21:17 -0800806 metadata["post-timestamp"] = post_timestamp
807
808
Geremy Condra36bd3652014-02-06 19:45:10 -0800809def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700810 # TODO(tbao): We should factor out the common parts between
811 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800812 source_version = OPTIONS.source_info_dict["recovery_api_version"]
813 target_version = OPTIONS.target_info_dict["recovery_api_version"]
814
815 if source_version == 0:
816 print ("WARNING: generating edify script for a source that "
817 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700818 script = edify_generator.EdifyGenerator(
819 source_version, OPTIONS.target_info_dict,
820 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800821
Tao Bao3806c232015-07-05 21:08:33 -0700822 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
823 recovery_mount_options = OPTIONS.source_info_dict.get(
824 "recovery_mount_options")
825 oem_dict = None
826 if oem_props is not None and len(oem_props) > 0:
827 if OPTIONS.oem_source is None:
828 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800829 if not OPTIONS.oem_no_mount:
830 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700831 oem_dict = common.LoadDictionaryFromLines(
832 open(OPTIONS.oem_source).readlines())
833
Dan Albert8b72aef2015-03-23 19:13:21 -0700834 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700835 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
836 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -0800837 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700838 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800839
Tao Bao99c17252017-02-07 11:21:17 -0800840 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -0800841
Geremy Condra36bd3652014-02-06 19:45:10 -0800842 device_specific = common.DeviceSpecificParams(
843 source_zip=source_zip,
844 source_version=source_version,
845 target_zip=target_zip,
846 target_version=target_version,
847 output_zip=output_zip,
848 script=script,
849 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700850 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800851
Tao Bao3806c232015-07-05 21:08:33 -0700852 source_fp = CalculateFingerprint(oem_props, oem_dict,
853 OPTIONS.source_info_dict)
854 target_fp = CalculateFingerprint(oem_props, oem_dict,
855 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800856 metadata["pre-build"] = source_fp
857 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700858 metadata["pre-build-incremental"] = GetBuildProp(
859 "ro.build.version.incremental", OPTIONS.source_info_dict)
860 metadata["post-build-incremental"] = GetBuildProp(
861 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800862
863 source_boot = common.GetBootableImage(
864 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
865 OPTIONS.source_info_dict)
866 target_boot = common.GetBootableImage(
867 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
868 updating_boot = (not OPTIONS.two_step and
869 (source_boot.data != target_boot.data))
870
Geremy Condra36bd3652014-02-06 19:45:10 -0800871 target_recovery = common.GetBootableImage(
872 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800873
Doug Zongkerfc44a512014-08-26 13:10:25 -0700874 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
875 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700876
877 blockimgdiff_version = 1
878 if OPTIONS.info_dict:
879 blockimgdiff_version = max(
880 int(i) for i in
881 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
882
Tianjie Xufc3422a2015-12-15 11:53:59 -0800883 # Check first block of system partition for remount R/W only if
884 # disk type is ext4
885 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
Tao Baob4cfca52016-02-04 14:26:02 -0800886 check_first_block = system_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700887 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
888 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
889 # b) the blocks listed in block map may not contain all the bytes for a given
890 # file (because they're rounded to be 4K-aligned).
891 disable_imgdiff = system_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700892 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800893 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700894 version=blockimgdiff_version,
895 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700896
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700897 if HasVendorPartition(target_zip):
898 if not HasVendorPartition(source_zip):
899 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700900 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
901 OPTIONS.source_info_dict)
902 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
903 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800904
905 # Check first block of vendor partition for remount R/W only if
906 # disk type is ext4
907 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baob4cfca52016-02-04 14:26:02 -0800908 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700909 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700910 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800911 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700912 version=blockimgdiff_version,
913 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700914 else:
915 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800916
Michael Rungec6e3afd2014-05-05 11:55:47 -0700917 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800918 device_specific.IncrementalOTA_Assertions()
919
920 # Two-step incremental package strategy (in chronological order,
921 # which is *not* the order in which the generated script has
922 # things):
923 #
924 # if stage is not "2/3" or "3/3":
925 # do verification on current system
926 # write recovery image to boot partition
927 # set stage to "2/3"
928 # reboot to boot partition and restart recovery
929 # else if stage is "2/3":
930 # write recovery image to recovery partition
931 # set stage to "3/3"
932 # reboot to recovery partition and restart recovery
933 # else:
934 # (stage must be "3/3")
935 # perform update:
936 # patch system files, etc.
937 # force full install of new boot image
938 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700939 # complete script normally
940 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800941
942 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700943 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800944 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700945 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800946 assert fs.fs_type.upper() == "EMMC", \
947 "two-step packages only supported on devices with EMMC /misc partitions"
948 bcb_dev = {"bcb_dev": fs.device}
949 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
950 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700951if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800952""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700953 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800954 script.WriteRawImage("/recovery", "recovery.img")
955 script.AppendExtra("""
956set_stage("%(bcb_dev)s", "3/3");
957reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700958else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800959""" % bcb_dev)
960
Tao Bao6c55a8a2015-04-08 15:30:27 -0700961 # Dump fingerprints
962 script.Print("Source: %s" % CalculateFingerprint(
963 oem_props, oem_dict, OPTIONS.source_info_dict))
964 script.Print("Target: %s" % CalculateFingerprint(
965 oem_props, oem_dict, OPTIONS.target_info_dict))
966
Geremy Condra36bd3652014-02-06 19:45:10 -0800967 script.Print("Verifying current system...")
968
969 device_specific.IncrementalOTA_VerifyBegin()
970
Michael Rungec6e3afd2014-05-05 11:55:47 -0700971 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700972 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
973 # patching on a device that's already on the target build will damage the
974 # system. Because operations like move don't check the block state, they
975 # always apply the changes unconditionally.
976 if blockimgdiff_version <= 2:
977 script.AssertSomeFingerprint(source_fp)
978 else:
979 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700980 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700981 if blockimgdiff_version <= 2:
982 script.AssertSomeThumbprint(
983 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
984 else:
985 script.AssertSomeThumbprint(
986 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
987 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800988
Tao Baob4cfca52016-02-04 14:26:02 -0800989 # Check the required cache size (i.e. stashed blocks).
990 size = []
991 if system_diff:
992 size.append(system_diff.required_cache)
993 if vendor_diff:
994 size.append(vendor_diff.required_cache)
995
Geremy Condra36bd3652014-02-06 19:45:10 -0800996 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700997 boot_type, boot_device = common.GetTypeAndDevice(
998 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800999 d = common.Difference(target_boot, source_boot)
1000 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -07001001 if d is None:
1002 include_full_boot = True
1003 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1004 else:
1005 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -08001006
Doug Zongkerf8340082014-08-05 10:39:37 -07001007 print "boot target: %d source: %d diff: %d" % (
1008 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -08001009
Doug Zongkerf8340082014-08-05 10:39:37 -07001010 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -08001011
Doug Zongkerf8340082014-08-05 10:39:37 -07001012 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1013 (boot_type, boot_device,
1014 source_boot.size, source_boot.sha1,
1015 target_boot.size, target_boot.sha1))
Tao Baob4cfca52016-02-04 14:26:02 -08001016 size.append(target_boot.size)
1017
1018 if size:
1019 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001020
1021 device_specific.IncrementalOTA_VerifyEnd()
1022
1023 if OPTIONS.two_step:
1024 script.WriteRawImage("/boot", "recovery.img")
1025 script.AppendExtra("""
1026set_stage("%(bcb_dev)s", "2/3");
1027reboot_now("%(bcb_dev)s", "");
1028else
1029""" % bcb_dev)
1030
Jesse Zhao75bcea02015-01-06 10:59:53 -08001031 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001032 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001033 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001034 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001035
Geremy Condra36bd3652014-02-06 19:45:10 -08001036 script.Comment("---- start making changes here ----")
1037
1038 device_specific.IncrementalOTA_InstallBegin()
1039
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001040 system_diff.WriteScript(script, output_zip,
1041 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001042
Doug Zongkerfc44a512014-08-26 13:10:25 -07001043 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001044 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001045
1046 if OPTIONS.two_step:
1047 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1048 script.WriteRawImage("/boot", "boot.img")
1049 print "writing full boot image (forced by two-step mode)"
1050
1051 if not OPTIONS.two_step:
1052 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001053 if include_full_boot:
1054 print "boot image changed; including full."
1055 script.Print("Installing boot image...")
1056 script.WriteRawImage("/boot", "boot.img")
1057 else:
1058 # Produce the boot image by applying a patch to the current
1059 # contents of the boot partition, and write it back to the
1060 # partition.
1061 print "boot image changed; including patch."
1062 script.Print("Patching boot image...")
1063 script.ShowProgress(0.1, 10)
1064 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1065 % (boot_type, boot_device,
1066 source_boot.size, source_boot.sha1,
1067 target_boot.size, target_boot.sha1),
1068 "-",
1069 target_boot.size, target_boot.sha1,
1070 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001071 else:
1072 print "boot image unchanged; skipping."
1073
1074 # Do device-specific installation (eg, write radio image).
1075 device_specific.IncrementalOTA_InstallEnd()
1076
1077 if OPTIONS.extra_script is not None:
1078 script.AppendExtra(OPTIONS.extra_script)
1079
Doug Zongker922206e2014-03-04 13:16:24 -08001080 if OPTIONS.wipe_user_data:
1081 script.Print("Erasing user data...")
1082 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001083 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001084
Geremy Condra36bd3652014-02-06 19:45:10 -08001085 if OPTIONS.two_step:
1086 script.AppendExtra("""
1087set_stage("%(bcb_dev)s", "");
1088endif;
1089endif;
1090""" % bcb_dev)
1091
1092 script.SetProgress(1)
Tao Baofa41fb22016-03-08 17:53:39 -08001093 # For downgrade OTAs, we prefer to use the update-binary in the source
1094 # build that is actually newer than the one in the target build.
1095 if OPTIONS.downgrade:
1096 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1097 else:
1098 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001099 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001100 WriteMetadata(metadata, output_zip)
1101
Doug Zongker32b527d2014-03-04 10:03:02 -08001102
Tao Bao9bc6bb22015-11-09 16:58:28 -08001103def WriteVerifyPackage(input_zip, output_zip):
1104 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1105
1106 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1107 recovery_mount_options = OPTIONS.info_dict.get(
1108 "recovery_mount_options")
1109 oem_dict = None
1110 if oem_props is not None and len(oem_props) > 0:
1111 if OPTIONS.oem_source is None:
1112 raise common.ExternalError("OEM source required for this build")
1113 script.Mount("/oem", recovery_mount_options)
1114 oem_dict = common.LoadDictionaryFromLines(
1115 open(OPTIONS.oem_source).readlines())
1116
1117 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1118 metadata = {
1119 "post-build": target_fp,
1120 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1121 OPTIONS.info_dict),
1122 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1123 }
1124
1125 device_specific = common.DeviceSpecificParams(
1126 input_zip=input_zip,
1127 input_version=OPTIONS.info_dict["recovery_api_version"],
1128 output_zip=output_zip,
1129 script=script,
1130 input_tmp=OPTIONS.input_tmp,
1131 metadata=metadata,
1132 info_dict=OPTIONS.info_dict)
1133
1134 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1135
1136 script.Print("Verifying device images against %s..." % target_fp)
1137 script.AppendExtra("")
1138
1139 script.Print("Verifying boot...")
1140 boot_img = common.GetBootableImage(
1141 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1142 boot_type, boot_device = common.GetTypeAndDevice(
1143 "/boot", OPTIONS.info_dict)
1144 script.Verify("%s:%s:%d:%s" % (
1145 boot_type, boot_device, boot_img.size, boot_img.sha1))
1146 script.AppendExtra("")
1147
1148 script.Print("Verifying recovery...")
1149 recovery_img = common.GetBootableImage(
1150 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1151 recovery_type, recovery_device = common.GetTypeAndDevice(
1152 "/recovery", OPTIONS.info_dict)
1153 script.Verify("%s:%s:%d:%s" % (
1154 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1155 script.AppendExtra("")
1156
1157 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1158 system_tgt.ResetFileMap()
1159 system_diff = common.BlockDifference("system", system_tgt, src=None)
1160 system_diff.WriteStrictVerifyScript(script)
1161
1162 if HasVendorPartition(input_zip):
1163 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1164 vendor_tgt.ResetFileMap()
1165 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1166 vendor_diff.WriteStrictVerifyScript(script)
1167
1168 # Device specific partitions, such as radio, bootloader and etc.
1169 device_specific.VerifyOTA_Assertions()
1170
1171 script.SetProgress(1.0)
1172 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001173 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001174 WriteMetadata(metadata, output_zip)
1175
1176
Tao Baoc098e9e2016-01-07 13:03:56 -08001177def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1178 source_file=None):
1179 """Generate an Android OTA package that has A/B update payload."""
1180
1181 # Setup signing keys.
1182 if OPTIONS.package_key is None:
1183 OPTIONS.package_key = OPTIONS.info_dict.get(
1184 "default_system_dev_certificate",
1185 "build/target/product/security/testkey")
1186
1187 # A/B updater expects key in RSA format.
1188 cmd = ["openssl", "pkcs8",
1189 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1190 "-inform", "DER", "-nocrypt"]
1191 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1192 cmd.extend(["-out", rsa_key])
1193 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1194 p1.wait()
1195 assert p1.returncode == 0, "openssl pkcs8 failed"
1196
1197 # Stage the output zip package for signing.
1198 temp_zip_file = tempfile.NamedTemporaryFile()
1199 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1200 compression=zipfile.ZIP_DEFLATED)
1201
1202 # Metadata to comply with Android OTA package format.
1203 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1204 oem_dict = None
1205 if oem_props:
1206 if OPTIONS.oem_source is None:
1207 raise common.ExternalError("OEM source required for this build")
1208 oem_dict = common.LoadDictionaryFromLines(
1209 open(OPTIONS.oem_source).readlines())
1210
1211 metadata = {
1212 "post-build": CalculateFingerprint(oem_props, oem_dict,
1213 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001214 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1215 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001216 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1217 OPTIONS.info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001218 "ota-required-cache": "0",
1219 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001220 }
1221
1222 if source_file is not None:
1223 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1224 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001225 metadata["pre-build-incremental"] = GetBuildProp(
1226 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001227
Tao Bao99c17252017-02-07 11:21:17 -08001228 HandleDowngradeMetadata(metadata)
1229 else:
1230 metadata["post-timestamp"] = GetBuildProp(
1231 "ro.build.date.utc", OPTIONS.info_dict)
1232
Tao Baoc098e9e2016-01-07 13:03:56 -08001233 # 1. Generate payload.
1234 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1235 cmd = ["brillo_update_payload", "generate",
1236 "--payload", payload_file,
1237 "--target_image", target_file]
1238 if source_file is not None:
1239 cmd.extend(["--source_image", source_file])
1240 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1241 p1.wait()
1242 assert p1.returncode == 0, "brillo_update_payload generate failed"
1243
1244 # 2. Generate hashes of the payload and metadata files.
1245 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1246 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1247 cmd = ["brillo_update_payload", "hash",
1248 "--unsigned_payload", payload_file,
1249 "--signature_size", "256",
1250 "--metadata_hash_file", metadata_sig_file,
1251 "--payload_hash_file", payload_sig_file]
1252 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1253 p1.wait()
1254 assert p1.returncode == 0, "brillo_update_payload hash failed"
1255
1256 # 3. Sign the hashes and insert them back into the payload file.
1257 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1258 suffix=".bin")
1259 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1260 suffix=".bin")
1261 # 3a. Sign the payload hash.
1262 cmd = ["openssl", "pkeyutl", "-sign",
1263 "-inkey", rsa_key,
1264 "-pkeyopt", "digest:sha256",
1265 "-in", payload_sig_file,
1266 "-out", signed_payload_sig_file]
1267 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1268 p1.wait()
1269 assert p1.returncode == 0, "openssl sign payload failed"
1270
1271 # 3b. Sign the metadata hash.
1272 cmd = ["openssl", "pkeyutl", "-sign",
1273 "-inkey", rsa_key,
1274 "-pkeyopt", "digest:sha256",
1275 "-in", metadata_sig_file,
1276 "-out", signed_metadata_sig_file]
1277 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1278 p1.wait()
1279 assert p1.returncode == 0, "openssl sign metadata failed"
1280
1281 # 3c. Insert the signatures back into the payload file.
1282 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1283 suffix=".bin")
1284 cmd = ["brillo_update_payload", "sign",
1285 "--unsigned_payload", payload_file,
1286 "--payload", signed_payload_file,
1287 "--signature_size", "256",
1288 "--metadata_signature_file", signed_metadata_sig_file,
1289 "--payload_signature_file", signed_payload_sig_file]
1290 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1291 p1.wait()
1292 assert p1.returncode == 0, "brillo_update_payload sign failed"
1293
Alex Deymo19241c12016-02-04 22:29:29 -08001294 # 4. Dump the signed payload properties.
1295 properties_file = common.MakeTempFile(prefix="payload-properties-",
1296 suffix=".txt")
1297 cmd = ["brillo_update_payload", "properties",
1298 "--payload", signed_payload_file,
1299 "--properties_file", properties_file]
1300 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1301 p1.wait()
1302 assert p1.returncode == 0, "brillo_update_payload properties failed"
1303
1304 # Add the signed payload file and properties into the zip.
1305 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001306 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1307 compress_type=zipfile.ZIP_STORED)
1308 WriteMetadata(metadata, output_zip)
1309
1310 # Sign the whole package to comply with the Android OTA package format.
1311 common.ZipClose(output_zip)
1312 SignOutput(temp_zip_file.name, output_file)
1313 temp_zip_file.close()
1314
1315
Dan Albert8b72aef2015-03-23 19:13:21 -07001316class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001317 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001318 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001319 print "Loading target..."
1320 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1321 print "Loading source..."
1322 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1323
1324 self.verbatim_targets = verbatim_targets = []
1325 self.patch_list = patch_list = []
1326 diffs = []
1327 self.renames = renames = {}
1328 known_paths = set()
1329 largest_source_size = 0
1330
1331 matching_file_cache = {}
1332 for fn, sf in source_data.items():
1333 assert fn == sf.name
1334 matching_file_cache["path:" + fn] = sf
1335 if fn in target_data.keys():
1336 AddToKnownPaths(fn, known_paths)
1337 # Only allow eligibility for filename/sha matching
1338 # if there isn't a perfect path match.
1339 if target_data.get(sf.name) is None:
1340 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1341 matching_file_cache["sha:" + sf.sha1] = sf
1342
1343 for fn in sorted(target_data.keys()):
1344 tf = target_data[fn]
1345 assert fn == tf.name
1346 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1347 if sf is not None and sf.name != tf.name:
1348 print "File has moved from " + sf.name + " to " + tf.name
1349 renames[sf.name] = tf
1350
1351 if sf is None or fn in OPTIONS.require_verbatim:
1352 # This file should be included verbatim
1353 if fn in OPTIONS.prohibit_verbatim:
1354 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1355 print "send", fn, "verbatim"
1356 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001357 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001358 if fn in target_data.keys():
1359 AddToKnownPaths(fn, known_paths)
1360 elif tf.sha1 != sf.sha1:
1361 # File is different; consider sending as a patch
1362 diffs.append(common.Difference(tf, sf))
1363 else:
1364 # Target file data identical to source (may still be renamed)
1365 pass
1366
1367 common.ComputeDifferences(diffs)
1368
1369 for diff in diffs:
1370 tf, sf, d = diff.GetPatch()
1371 path = "/".join(tf.name.split("/")[:-1])
1372 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1373 path not in known_paths:
1374 # patch is almost as big as the file; don't bother patching
1375 # or a patch + rename cannot take place due to the target
1376 # directory not existing
1377 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001378 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001379 if sf.name in renames:
1380 del renames[sf.name]
1381 AddToKnownPaths(tf.name, known_paths)
1382 else:
1383 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1384 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1385 largest_source_size = max(largest_source_size, sf.size)
1386
1387 self.largest_source_size = largest_source_size
1388
1389 def EmitVerification(self, script):
1390 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001391 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001392 if tf.name != sf.name:
1393 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1394 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1395 so_far += sf.size
1396 return so_far
1397
Michael Runge63f01de2014-10-28 19:24:19 -07001398 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001399 for fn, _, sha1 in self.verbatim_targets:
1400 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001401 script.FileCheck("/"+fn, sha1)
1402 for tf, _, _, _ in self.patch_list:
1403 script.FileCheck(tf.name, tf.sha1)
1404
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001405 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001406 file_list = ["/" + i[0] for i in self.verbatim_targets]
1407 file_list += ["/" + i for i in self.source_data
1408 if i not in self.target_data and i not in self.renames]
1409 file_list += list(extras)
1410 # Sort the list in descending order, which removes all the files first
1411 # before attempting to remove the folder. (Bug: 22960996)
1412 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001413
1414 def TotalPatchSize(self):
1415 return sum(i[1].size for i in self.patch_list)
1416
1417 def EmitPatches(self, script, total_patch_size, so_far):
1418 self.deferred_patch_list = deferred_patch_list = []
1419 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001420 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001421 if tf.name == "system/build.prop":
1422 deferred_patch_list.append(item)
1423 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001424 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001425 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001426 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1427 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001428 so_far += tf.size
1429 script.SetProgress(so_far / total_patch_size)
1430 return so_far
1431
1432 def EmitDeferredPatches(self, script):
1433 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001434 tf, sf, _, _ = item
1435 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1436 "patch/" + sf.name + ".p")
1437 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001438
1439 def EmitRenames(self, script):
1440 if len(self.renames) > 0:
1441 script.Print("Renaming files...")
1442 for src, tgt in self.renames.iteritems():
1443 print "Renaming " + src + " to " + tgt.name
1444 script.RenameFile(src, tgt.name)
1445
1446
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001447def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001448 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1449 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1450
Doug Zongker26e66192014-02-20 13:22:07 -08001451 if (OPTIONS.block_based and
1452 target_has_recovery_patch and
1453 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001454 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1455
Doug Zongker37974732010-09-16 17:44:38 -07001456 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1457 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001458
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001459 if source_version == 0:
1460 print ("WARNING: generating edify script for a source that "
1461 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001462 script = edify_generator.EdifyGenerator(
1463 source_version, OPTIONS.target_info_dict,
1464 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001465
Michael Runge6e836112014-04-15 17:40:21 -07001466 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001467 recovery_mount_options = OPTIONS.source_info_dict.get(
1468 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001469 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001470 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001471 if OPTIONS.oem_source is None:
1472 raise common.ExternalError("OEM source required for this build")
Tao Baobd25fcd2016-03-07 21:24:40 -08001473 if not OPTIONS.oem_no_mount:
1474 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001475 oem_dict = common.LoadDictionaryFromLines(
1476 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001477
Dan Albert8b72aef2015-03-23 19:13:21 -07001478 metadata = {
1479 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1480 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001481 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001482 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001483
Tao Bao99c17252017-02-07 11:21:17 -08001484 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -08001485
Doug Zongker05d3dea2009-06-22 11:32:31 -07001486 device_specific = common.DeviceSpecificParams(
1487 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001488 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001489 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001490 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001491 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001492 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001493 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001494 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001495
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001496 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001497 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001498 if HasVendorPartition(target_zip):
1499 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001500 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001501 else:
1502 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001503
Dan Albert8b72aef2015-03-23 19:13:21 -07001504 target_fp = CalculateFingerprint(oem_props, oem_dict,
1505 OPTIONS.target_info_dict)
1506 source_fp = CalculateFingerprint(oem_props, oem_dict,
1507 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001508
1509 if oem_props is None:
1510 script.AssertSomeFingerprint(source_fp, target_fp)
1511 else:
1512 script.AssertSomeThumbprint(
1513 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1514 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1515
Doug Zongker2ea21062010-04-28 16:05:21 -07001516 metadata["pre-build"] = source_fp
1517 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001518 metadata["pre-build-incremental"] = GetBuildProp(
1519 "ro.build.version.incremental", OPTIONS.source_info_dict)
1520 metadata["post-build-incremental"] = GetBuildProp(
1521 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001522
Doug Zongker55d93282011-01-25 17:03:34 -08001523 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001524 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1525 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001526 target_boot = common.GetBootableImage(
1527 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001528 updating_boot = (not OPTIONS.two_step and
1529 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001530
Doug Zongker55d93282011-01-25 17:03:34 -08001531 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001532 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1533 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001534 target_recovery = common.GetBootableImage(
1535 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001536 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001537
Doug Zongker881dd402009-09-20 14:03:55 -07001538 # Here's how we divide up the progress bar:
1539 # 0.1 for verifying the start state (PatchCheck calls)
1540 # 0.8 for applying patches (ApplyPatch calls)
1541 # 0.1 for unpacking verbatim files, symlinking, and doing the
1542 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001543
Michael Runge6e836112014-04-15 17:40:21 -07001544 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001545 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001546
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001547 # Two-step incremental package strategy (in chronological order,
1548 # which is *not* the order in which the generated script has
1549 # things):
1550 #
1551 # if stage is not "2/3" or "3/3":
1552 # do verification on current system
1553 # write recovery image to boot partition
1554 # set stage to "2/3"
1555 # reboot to boot partition and restart recovery
1556 # else if stage is "2/3":
1557 # write recovery image to recovery partition
1558 # set stage to "3/3"
1559 # reboot to recovery partition and restart recovery
1560 # else:
1561 # (stage must be "3/3")
1562 # perform update:
1563 # patch system files, etc.
1564 # force full install of new boot image
1565 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001566 # complete script normally
1567 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001568
1569 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001570 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001571 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001572 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001573 assert fs.fs_type.upper() == "EMMC", \
1574 "two-step packages only supported on devices with EMMC /misc partitions"
1575 bcb_dev = {"bcb_dev": fs.device}
1576 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1577 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001578if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001579""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001580 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001581 script.WriteRawImage("/recovery", "recovery.img")
1582 script.AppendExtra("""
1583set_stage("%(bcb_dev)s", "3/3");
1584reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001585else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001586""" % bcb_dev)
1587
Tao Bao6c55a8a2015-04-08 15:30:27 -07001588 # Dump fingerprints
1589 script.Print("Source: %s" % (source_fp,))
1590 script.Print("Target: %s" % (target_fp,))
1591
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001592 script.Print("Verifying current system...")
1593
Doug Zongkere5ff5902012-01-17 10:55:37 -08001594 device_specific.IncrementalOTA_VerifyBegin()
1595
Doug Zongker881dd402009-09-20 14:03:55 -07001596 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001597 so_far = system_diff.EmitVerification(script)
1598 if vendor_diff:
1599 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001600
Tao Baob4cfca52016-02-04 14:26:02 -08001601 size = []
1602 if system_diff.patch_list:
1603 size.append(system_diff.largest_source_size)
1604 if vendor_diff:
1605 if vendor_diff.patch_list:
1606 size.append(vendor_diff.largest_source_size)
1607
Doug Zongker5da317e2009-06-02 13:38:17 -07001608 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001609 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001610 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001611 print "boot target: %d source: %d diff: %d" % (
1612 target_boot.size, source_boot.size, len(d))
1613
Doug Zongker048e7ca2009-06-15 14:31:53 -07001614 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001615
Tao Baodd24da92015-07-29 14:09:23 -07001616 boot_type, boot_device = common.GetTypeAndDevice(
1617 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001618
1619 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1620 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001621 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001622 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001623 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001624 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001625
Tao Baob4cfca52016-02-04 14:26:02 -08001626 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001627 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001628
Doug Zongker05d3dea2009-06-22 11:32:31 -07001629 device_specific.IncrementalOTA_VerifyEnd()
1630
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001631 if OPTIONS.two_step:
1632 script.WriteRawImage("/boot", "recovery.img")
1633 script.AppendExtra("""
1634set_stage("%(bcb_dev)s", "2/3");
1635reboot_now("%(bcb_dev)s", "");
1636else
1637""" % bcb_dev)
1638
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001639 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001640
Doug Zongkere5ff5902012-01-17 10:55:37 -08001641 device_specific.IncrementalOTA_InstallBegin()
1642
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001643 if OPTIONS.two_step:
1644 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1645 script.WriteRawImage("/boot", "boot.img")
1646 print "writing full boot image (forced by two-step mode)"
1647
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001648 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001649 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1650 if vendor_diff:
1651 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001652
Doug Zongker881dd402009-09-20 14:03:55 -07001653 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001654 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1655 if vendor_diff:
1656 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001657 if updating_boot:
1658 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001659
1660 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001661 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1662 if vendor_diff:
1663 script.Print("Patching vendor files...")
1664 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001665
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001666 if not OPTIONS.two_step:
1667 if updating_boot:
1668 # Produce the boot image by applying a patch to the current
1669 # contents of the boot partition, and write it back to the
1670 # partition.
1671 script.Print("Patching boot image...")
1672 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1673 % (boot_type, boot_device,
1674 source_boot.size, source_boot.sha1,
1675 target_boot.size, target_boot.sha1),
1676 "-",
1677 target_boot.size, target_boot.sha1,
1678 source_boot.sha1, "patch/boot.img.p")
1679 so_far += target_boot.size
1680 script.SetProgress(so_far / total_patch_size)
1681 print "boot image changed; including."
1682 else:
1683 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001684
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001685 system_items = ItemSet("system", "META/filesystem_config.txt")
1686 if vendor_diff:
1687 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1688
Doug Zongkereef39442009-04-02 12:14:19 -07001689 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001690 # Recovery is generated as a patch using both the boot image
1691 # (which contains the same linux kernel as recovery) and the file
1692 # /system/etc/recovery-resource.dat (which contains all the images
1693 # used in the recovery UI) as sources. This lets us minimize the
1694 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001695 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001696 # For older builds where recovery-resource.dat is not present, we
1697 # use only the boot image as the source.
1698
Doug Zongkerc9253822014-02-04 12:17:58 -08001699 if not target_has_recovery_patch:
1700 def output_sink(fn, data):
1701 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001702 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001703
1704 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1705 target_recovery, target_boot)
1706 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001707 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001708 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001709 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001710 else:
1711 print "recovery image unchanged; skipping."
1712
Doug Zongker881dd402009-09-20 14:03:55 -07001713 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001714
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001715 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1716 if vendor_diff:
1717 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1718
1719 temp_script = script.MakeTemporary()
1720 system_items.GetMetadata(target_zip)
1721 system_items.Get("system").SetPermissions(temp_script)
1722 if vendor_diff:
1723 vendor_items.GetMetadata(target_zip)
1724 vendor_items.Get("vendor").SetPermissions(temp_script)
1725
1726 # Note that this call will mess up the trees of Items, so make sure
1727 # we're done with them.
1728 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1729 if vendor_diff:
1730 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001731
1732 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001733 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1734
1735 # Delete all the symlinks in source that aren't in target. This
1736 # needs to happen before verbatim files are unpacked, in case a
1737 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001738
1739 # If a symlink in the source will be replaced by a regular file, we cannot
1740 # delete the symlink/file in case the package gets applied again. For such
1741 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1742 # (Bug: 23646151)
1743 replaced_symlinks = dict()
1744 if system_diff:
1745 for i in system_diff.verbatim_targets:
1746 replaced_symlinks["/%s" % (i[0],)] = i[2]
1747 if vendor_diff:
1748 for i in vendor_diff.verbatim_targets:
1749 replaced_symlinks["/%s" % (i[0],)] = i[2]
1750
1751 if system_diff:
1752 for tf in system_diff.renames.values():
1753 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1754 if vendor_diff:
1755 for tf in vendor_diff.renames.values():
1756 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1757
1758 always_delete = []
1759 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001760 for dest, link in source_symlinks:
1761 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001762 if link in replaced_symlinks:
1763 may_delete.append((link, replaced_symlinks[link]))
1764 else:
1765 always_delete.append(link)
1766 script.DeleteFiles(always_delete)
1767 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001768
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001769 if system_diff.verbatim_targets:
1770 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001771 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001772 if vendor_diff and vendor_diff.verbatim_targets:
1773 script.Print("Unpacking new vendor files...")
1774 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001775
Doug Zongkerc9253822014-02-04 12:17:58 -08001776 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001777 script.Print("Unpacking new recovery...")
1778 script.UnpackPackageDir("recovery", "/system")
1779
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001780 system_diff.EmitRenames(script)
1781 if vendor_diff:
1782 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001783
Doug Zongker05d3dea2009-06-22 11:32:31 -07001784 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001785
1786 # Create all the symlinks that don't already exist, or point to
1787 # somewhere different than what we want. Delete each symlink before
1788 # creating it, since the 'symlink' command won't overwrite.
1789 to_create = []
1790 for dest, link in target_symlinks:
1791 if link in source_symlinks_d:
1792 if dest != source_symlinks_d[link]:
1793 to_create.append((dest, link))
1794 else:
1795 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001796 script.DeleteFiles([i[1] for i in to_create])
1797 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001798
1799 # Now that the symlinks are created, we can set all the
1800 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001801 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001802
Doug Zongker881dd402009-09-20 14:03:55 -07001803 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001804 device_specific.IncrementalOTA_InstallEnd()
1805
Doug Zongker1c390a22009-05-14 19:06:36 -07001806 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001807 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001808
Doug Zongkere92f15a2011-08-26 13:46:40 -07001809 # Patch the build.prop file last, so if something fails but the
1810 # device can still come up, it appears to be the old build and will
1811 # get set the OTA package again to retry.
1812 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001813 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001814
Doug Zongker922206e2014-03-04 13:16:24 -08001815 if OPTIONS.wipe_user_data:
1816 script.Print("Erasing user data...")
1817 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001818 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001819
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001820 if OPTIONS.two_step:
1821 script.AppendExtra("""
1822set_stage("%(bcb_dev)s", "");
1823endif;
1824endif;
1825""" % bcb_dev)
1826
Michael Runge63f01de2014-10-28 19:24:19 -07001827 if OPTIONS.verify and system_diff:
1828 script.Print("Remounting and verifying system partition files...")
1829 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001830 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001831 system_diff.EmitExplicitTargetVerification(script)
1832
1833 if OPTIONS.verify and vendor_diff:
1834 script.Print("Remounting and verifying vendor partition files...")
1835 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001836 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001837 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001838
1839 # For downgrade OTAs, we prefer to use the update-binary in the source
1840 # build that is actually newer than the one in the target build.
1841 if OPTIONS.downgrade:
1842 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1843 else:
1844 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001845
Tao Baob4cfca52016-02-04 14:26:02 -08001846 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001847 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001848
1849
1850def main(argv):
1851
1852 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001853 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001854 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001855 elif o in ("-k", "--package_key"):
1856 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001857 elif o in ("-i", "--incremental_from"):
1858 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001859 elif o == "--full_radio":
1860 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001861 elif o == "--full_bootloader":
1862 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001863 elif o in ("-w", "--wipe_user_data"):
1864 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001865 elif o in ("-n", "--no_prereq"):
1866 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08001867 elif o == "--downgrade":
1868 OPTIONS.downgrade = True
1869 OPTIONS.wipe_user_data = True
Tao Bao9f884e62017-02-28 11:48:48 -08001870 elif o == "--override_timestamp":
1871 OPTIONS.timestamp = True
Michael Runge6e836112014-04-15 17:40:21 -07001872 elif o in ("-o", "--oem_settings"):
1873 OPTIONS.oem_source = a
Tao Baodf4cb0b2016-02-25 19:49:55 -08001874 elif o == "--oem_no_mount":
1875 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001876 elif o in ("-e", "--extra_script"):
1877 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001878 elif o in ("-a", "--aslr_mode"):
1879 if a in ("on", "On", "true", "True", "yes", "Yes"):
1880 OPTIONS.aslr_mode = True
1881 else:
1882 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001883 elif o in ("-t", "--worker_threads"):
1884 if a.isdigit():
1885 OPTIONS.worker_threads = int(a)
1886 else:
1887 raise ValueError("Cannot parse value %r for option %r - only "
1888 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001889 elif o in ("-2", "--two_step"):
1890 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001891 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001892 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001893 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001894 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001895 elif o == "--block":
1896 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001897 elif o in ("-b", "--binary"):
1898 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001899 elif o in ("--no_fallback_to_full",):
1900 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001901 elif o == "--stash_threshold":
1902 try:
1903 OPTIONS.stash_threshold = float(a)
1904 except ValueError:
1905 raise ValueError("Cannot parse value %r for option %r - expecting "
1906 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001907 elif o == "--gen_verify":
1908 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001909 elif o == "--log_diff":
1910 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001911 else:
1912 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001913 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001914
1915 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001916 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001917 extra_long_opts=[
1918 "board_config=",
1919 "package_key=",
1920 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001921 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001922 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001923 "wipe_user_data",
1924 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08001925 "downgrade",
Tao Bao9f884e62017-02-28 11:48:48 -08001926 "override_timestamp",
Dan Albert8b72aef2015-03-23 19:13:21 -07001927 "extra_script=",
1928 "worker_threads=",
1929 "aslr_mode=",
1930 "two_step",
1931 "no_signing",
1932 "block",
1933 "binary=",
1934 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08001935 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001936 "verify",
1937 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001938 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001939 "gen_verify",
1940 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001941 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001942
1943 if len(args) != 2:
1944 common.Usage(__doc__)
1945 sys.exit(1)
1946
Tao Bao4da324e2016-02-23 11:38:39 -08001947 if OPTIONS.downgrade:
1948 # Sanity check to enforce a data wipe.
1949 if not OPTIONS.wipe_user_data:
1950 raise ValueError("Cannot downgrade without a data wipe")
1951
1952 # We should only allow downgrading incrementals (as opposed to full).
1953 # Otherwise the device may go back from arbitrary build with this full
1954 # OTA package.
1955 if OPTIONS.incremental_source is None:
1956 raise ValueError("Cannot generate downgradable full OTAs - consider"
1957 "using --omit_prereq?")
1958
Tao Bao9f884e62017-02-28 11:48:48 -08001959 assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
1960 "Cannot have --downgrade AND --override_timestamp both"
1961
Tao Baoc098e9e2016-01-07 13:03:56 -08001962 # Load the dict file from the zip directly to have a peek at the OTA type.
1963 # For packages using A/B update, unzipping is not needed.
1964 input_zip = zipfile.ZipFile(args[0], "r")
1965 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1966 common.ZipClose(input_zip)
1967
1968 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1969
1970 if ab_update:
1971 if OPTIONS.incremental_source is not None:
1972 OPTIONS.target_info_dict = OPTIONS.info_dict
1973 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1974 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1975 common.ZipClose(source_zip)
1976
1977 if OPTIONS.verbose:
1978 print "--- target info ---"
1979 common.DumpInfoDict(OPTIONS.info_dict)
1980
1981 if OPTIONS.incremental_source is not None:
1982 print "--- source info ---"
1983 common.DumpInfoDict(OPTIONS.source_info_dict)
1984
1985 WriteABOTAPackageWithBrilloScript(
1986 target_file=args[0],
1987 output_file=args[1],
1988 source_file=OPTIONS.incremental_source)
1989
1990 print "done."
1991 return
1992
Doug Zongker1c390a22009-05-14 19:06:36 -07001993 if OPTIONS.extra_script is not None:
1994 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1995
Doug Zongkereef39442009-04-02 12:14:19 -07001996 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001997 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001998
Doug Zongkereef39442009-04-02 12:14:19 -07001999 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002000 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002001
Doug Zongker37974732010-09-16 17:44:38 -07002002 if OPTIONS.verbose:
2003 print "--- target info ---"
2004 common.DumpInfoDict(OPTIONS.info_dict)
2005
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002006 # If the caller explicitly specified the device-specific extensions
2007 # path via -s/--device_specific, use that. Otherwise, use
2008 # META/releasetools.py if it is present in the target target_files.
2009 # Otherwise, take the path of the file from 'tool_extensions' in the
2010 # info dict and look for that in the local filesystem, relative to
2011 # the current directory.
2012
Doug Zongker37974732010-09-16 17:44:38 -07002013 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002014 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2015 if os.path.exists(from_input):
2016 print "(using device-specific extensions from target_files)"
2017 OPTIONS.device_specific = from_input
2018 else:
2019 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2020
Doug Zongker37974732010-09-16 17:44:38 -07002021 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002022 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002023
Tao Baoc098e9e2016-01-07 13:03:56 -08002024 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002025 raise common.ExternalError(
2026 "--- target build has specified no recovery ---")
2027
Tao Bao767e3ac2015-11-10 12:19:19 -08002028 # Use the default key to sign the package if not specified with package_key.
2029 if not OPTIONS.no_signing:
2030 if OPTIONS.package_key is None:
2031 OPTIONS.package_key = OPTIONS.info_dict.get(
2032 "default_system_dev_certificate",
2033 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002034
Tao Bao767e3ac2015-11-10 12:19:19 -08002035 # Set up the output zip. Create a temporary zip file if signing is needed.
2036 if OPTIONS.no_signing:
2037 if os.path.exists(args[1]):
2038 os.unlink(args[1])
2039 output_zip = zipfile.ZipFile(args[1], "w",
2040 compression=zipfile.ZIP_DEFLATED)
2041 else:
2042 temp_zip_file = tempfile.NamedTemporaryFile()
2043 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2044 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002045
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002046 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002047 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002048 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002049 print "--- can't determine the cache partition size ---"
2050 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002051
Tao Bao9bc6bb22015-11-09 16:58:28 -08002052 # Generate a verify package.
2053 if OPTIONS.gen_verify:
2054 WriteVerifyPackage(input_zip, output_zip)
2055
Tao Bao767e3ac2015-11-10 12:19:19 -08002056 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002057 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002058 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002059
2060 # Generate an incremental OTA. It will fall back to generate a full OTA on
2061 # failure unless no_fallback_to_full is specified.
2062 else:
2063 print "unzipping source target-files..."
2064 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2065 OPTIONS.incremental_source)
2066 OPTIONS.target_info_dict = OPTIONS.info_dict
2067 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2068 OPTIONS.source_tmp)
2069 if OPTIONS.verbose:
2070 print "--- source info ---"
2071 common.DumpInfoDict(OPTIONS.source_info_dict)
2072 try:
2073 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002074 if OPTIONS.log_diff:
2075 out_file = open(OPTIONS.log_diff, 'w')
2076 import target_files_diff
2077 target_files_diff.recursiveDiff('',
2078 OPTIONS.source_tmp,
2079 OPTIONS.input_tmp,
2080 out_file)
2081 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002082 except ValueError:
2083 if not OPTIONS.fallback_to_full:
2084 raise
2085 print "--- failed to build incremental; falling back to full ---"
2086 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002087 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002088
Tao Bao767e3ac2015-11-10 12:19:19 -08002089 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002090
Tao Bao767e3ac2015-11-10 12:19:19 -08002091 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002092 if not OPTIONS.no_signing:
2093 SignOutput(temp_zip_file.name, args[1])
2094 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002095
Doug Zongkereef39442009-04-02 12:14:19 -07002096 print "done."
2097
2098
2099if __name__ == '__main__':
2100 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002101 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002102 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002103 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002104 print
2105 print " ERROR: %s" % (e,)
2106 print
2107 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002108 finally:
2109 common.Cleanup()