blob: 0a0173dcd3dd3029fb2bb482f1efe75215426890 [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
78 the OTA package, unless --binary flag is specified.
Tao Bao4da324e2016-02-23 11:38:39 -080079
Doug Zongker1c390a22009-05-14 19:06:36 -070080 -e (--extra_script) <file>
81 Insert the contents of file at the end of the update script.
82
Hristo Bojinovdafb0422010-08-26 14:35:16 -070083 -a (--aslr_mode) <on|off>
84 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050085
Doug Zongker9b23f2c2013-11-25 14:44:12 -080086 -2 (--two_step)
87 Generate a 'two-step' OTA package, where recovery is updated
88 first, so that any changes made to the system partition are done
89 using the new recovery (new kernel, etc.).
90
Doug Zongker26e66192014-02-20 13:22:07 -080091 --block
92 Generate a block-based OTA if possible. Will fall back to a
93 file-based OTA if the target_files is older and doesn't support
94 block-based OTAs.
95
Doug Zongker25568482014-03-03 10:21:27 -080096 -b (--binary) <file>
97 Use the given binary as the update-binary in the output package,
98 instead of the binary in the build's target_files. Use for
99 development only.
100
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200101 -t (--worker_threads) <int>
102 Specifies the number of worker-threads that will be used when
103 generating patches for incremental updates (defaults to 3).
104
Tao Bao8dcf7382015-05-21 14:09:49 -0700105 --stash_threshold <float>
106 Specifies the threshold that will be used to compute the maximum
107 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800108
109 --gen_verify
110 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800111
112 --log_diff <file>
113 Generate a log file that shows the differences in the source and target
114 builds for an incremental package. This option is only meaningful when
115 -i is specified.
Tao Baodea0f8b2016-06-20 17:55:06 -0700116
117 --payload_signer <signer>
118 Specify the signer when signing the payload and metadata for A/B OTAs.
119 By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
120 with the package private key. If the private key cannot be accessed
121 directly, a payload signer that knows how to do that should be specified.
122 The signer will be supplied with "-inkey <path_to_key>",
123 "-in <input_file>" and "-out <output_file>" parameters.
Doug Zongkereef39442009-04-02 12:14:19 -0700124"""
125
126import sys
127
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800128if sys.hexversion < 0x02070000:
129 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700130 sys.exit(1)
131
Doug Zongkerfc44a512014-08-26 13:10:25 -0700132import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700133import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800134import subprocess
Doug Zongkereef39442009-04-02 12:14:19 -0700135import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700136import zipfile
137
138import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700139import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700140import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700141
142OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700143OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700144OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700145OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700146OPTIONS.require_verbatim = set()
147OPTIONS.prohibit_verbatim = set(("system/build.prop",))
148OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700149OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700150OPTIONS.omit_prereq = False
Tao Bao4da324e2016-02-23 11:38:39 -0800151OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700152OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700153OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700154OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
155if OPTIONS.worker_threads == 0:
156 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800157OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900158OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800159OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800160OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700161OPTIONS.oem_source = None
Tao Baodf4cb0b2016-02-25 19:49:55 -0800162OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700163OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700164OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700165OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700166# Stash size cannot exceed cache_size * threshold.
167OPTIONS.cache_size = None
168OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800169OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800170OPTIONS.log_diff = None
Tao Baodea0f8b2016-06-20 17:55:06 -0700171OPTIONS.payload_signer = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700172
Doug Zongkereef39442009-04-02 12:14:19 -0700173def MostPopularKey(d, default):
174 """Given a dict, return the key corresponding to the largest
175 value. Returns 'default' if the dict is empty."""
176 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700177 if not x:
178 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700179 x.sort()
180 return x[-1][1]
181
182
183def IsSymlink(info):
184 """Return true if the zipfile.ZipInfo object passed in represents a
185 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700186 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700187
Hristo Bojinov96be7202010-08-02 10:26:17 -0700188def IsRegular(info):
189 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700190 regular file."""
191 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700192
Michael Runge4038aa82013-12-13 18:06:28 -0800193def ClosestFileMatch(src, tgtfiles, existing):
194 """Returns the closest file match between a source file and list
195 of potential matches. The exact filename match is preferred,
196 then the sha1 is searched for, and finally a file with the same
197 basename is evaluated. Rename support in the updater-binary is
198 required for the latter checks to be used."""
199
200 result = tgtfiles.get("path:" + src.name)
201 if result is not None:
202 return result
203
204 if not OPTIONS.target_info_dict.get("update_rename_support", False):
205 return None
206
207 if src.size < 1000:
208 return None
209
210 result = tgtfiles.get("sha1:" + src.sha1)
211 if result is not None and existing.get(result.name) is None:
212 return result
213 result = tgtfiles.get("file:" + src.name.split("/")[-1])
214 if result is not None and existing.get(result.name) is None:
215 return result
216 return None
217
Dan Albert8b72aef2015-03-23 19:13:21 -0700218class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700219 def __init__(self, partition, fs_config):
220 self.partition = partition
221 self.fs_config = fs_config
222 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700223
Dan Albert8b72aef2015-03-23 19:13:21 -0700224 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700225 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700226 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700227 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700228
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700229 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700230 # The target_files contains a record of what the uid,
231 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700232 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700233
234 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700235 if not line:
236 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700237 columns = line.split()
238 name, uid, gid, mode = columns[:4]
239 selabel = None
240 capabilities = None
241
242 # After the first 4 columns, there are a series of key=value
243 # pairs. Extract out the fields we care about.
244 for element in columns[4:]:
245 key, value = element.split("=")
246 if key == "selabel":
247 selabel = value
248 if key == "capabilities":
249 capabilities = value
250
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700251 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700252 if i is not None:
253 i.uid = int(uid)
254 i.gid = int(gid)
255 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700256 i.selabel = selabel
257 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700258 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700259 i.children.sort(key=lambda i: i.name)
260
Tao Baof2cffbd2015-07-22 12:33:18 -0700261 # Set metadata for the files generated by this script. For full recovery
262 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700263 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700264 if i:
265 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700266 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700267 if i:
268 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700269
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700270
Dan Albert8b72aef2015-03-23 19:13:21 -0700271class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700272 """Items represent the metadata (user, group, mode) of files and
273 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700274 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700275 self.itemset = itemset
276 self.name = name
277 self.uid = None
278 self.gid = None
279 self.mode = None
280 self.selabel = None
281 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700282 self.is_dir = is_dir
283 self.descendants = None
284 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700285
286 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700287 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700288 self.parent.children.append(self)
289 else:
290 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700291 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700292 self.children = []
293
294 def Dump(self, indent=0):
295 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700296 print "%s%s %d %d %o" % (
297 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700298 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700299 print "%s%s %s %s %s" % (
300 " " * indent, self.name, self.uid, self.gid, self.mode)
301 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700302 print "%s%s" % (" "*indent, self.descendants)
303 print "%s%s" % (" "*indent, self.best_subtree)
304 for i in self.children:
305 i.Dump(indent=indent+1)
306
Doug Zongkereef39442009-04-02 12:14:19 -0700307 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700308 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700309 all children and determine the best strategy for using set_perm_recursive
310 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700311 values. Recursively calls itself for all descendants.
312
Dan Albert8b72aef2015-03-23 19:13:21 -0700313 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
314 counting up all descendants of this node. (dmode or fmode may be None.)
315 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
316 fmode, selabel, capabilities) tuple that will match the most descendants of
317 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700318 """
319
Dan Albert8b72aef2015-03-23 19:13:21 -0700320 assert self.is_dir
321 key = (self.uid, self.gid, self.mode, None, self.selabel,
322 self.capabilities)
323 self.descendants = {key: 1}
324 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700325 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700326 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700327 for k, v in i.CountChildMetadata().iteritems():
328 d[k] = d.get(k, 0) + v
329 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700330 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700331 d[k] = d.get(k, 0) + 1
332
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700333 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
334 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700335
336 # First, find the (uid, gid) pair that matches the most
337 # descendants.
338 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700339 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700340 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
341 ug = MostPopularKey(ug, (0, 0))
342
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700343 # Now find the dmode, fmode, selabel, and capabilities that match
344 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700345 best_dmode = (0, 0o755)
346 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700347 best_selabel = (0, None)
348 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700349 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700350 if k[:2] != ug:
351 continue
352 if k[2] is not None and count >= best_dmode[0]:
353 best_dmode = (count, k[2])
354 if k[3] is not None and count >= best_fmode[0]:
355 best_fmode = (count, k[3])
356 if k[4] is not None and count >= best_selabel[0]:
357 best_selabel = (count, k[4])
358 if k[5] is not None and count >= best_capabilities[0]:
359 best_capabilities = (count, k[5])
360 self.best_subtree = ug + (
361 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700362
363 return d
364
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700365 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700366 """Append set_perm/set_perm_recursive commands to 'script' to
367 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700368 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700369
370 self.CountChildMetadata()
371
372 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700373 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
374 # that the current item (and all its children) have already been set to.
375 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700376 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700377 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700378 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700379 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700380 current = item.best_subtree
381
382 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700383 item.mode != current[2] or item.selabel != current[4] or \
384 item.capabilities != current[5]:
385 script.SetPermissions("/"+item.name, item.uid, item.gid,
386 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700387
388 for i in item.children:
389 recurse(i, current)
390 else:
391 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700392 item.mode != current[3] or item.selabel != current[4] or \
393 item.capabilities != current[5]:
394 script.SetPermissions("/"+item.name, item.uid, item.gid,
395 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700396
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700397 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700398
399
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700400def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
401 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700402 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800403 list of symlinks. output_zip may be None, in which case the copy is
404 skipped (but the other side effects still happen). substitute is an
405 optional dict of {output filename: contents} to be output instead of
406 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700407 """
408
409 symlinks = []
410
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700411 partition = itemset.partition
412
Doug Zongkereef39442009-04-02 12:14:19 -0700413 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700414 prefix = partition.upper() + "/"
415 if info.filename.startswith(prefix):
416 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700417 if IsSymlink(info):
418 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700419 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700420 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700421 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700422 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700423 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700424 if substitute and fn in substitute and substitute[fn] is None:
425 continue
426 if output_zip is not None:
427 if substitute and fn in substitute:
428 data = substitute[fn]
429 else:
430 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700431 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700432 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700433 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700434 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700435 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700436
437 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800438 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700439
440
Doug Zongkereef39442009-04-02 12:14:19 -0700441def SignOutput(temp_zip_name, output_zip_name):
442 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
443 pw = key_passwords[OPTIONS.package_key]
444
Doug Zongker951495f2009-08-14 12:44:19 -0700445 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
446 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700447
448
Dan Albert8b72aef2015-03-23 19:13:21 -0700449def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700450 oem_props = info_dict.get("oem_fingerprint_properties")
Michael Runge560569a2014-09-18 15:12:45 -0700451 if oem_props is None or len(oem_props) == 0:
Michael Runge6e836112014-04-15 17:40:21 -0700452 device = GetBuildProp("ro.product.device", info_dict)
453 script.AssertDevice(device)
454 else:
455 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700456 raise common.ExternalError(
457 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700458 for prop in oem_props.split():
459 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700460 raise common.ExternalError(
461 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700462 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700463
Doug Zongkereef39442009-04-02 12:14:19 -0700464
Doug Zongkerc9253822014-02-04 12:17:58 -0800465def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700466 namelist = [name for name in target_files_zip.namelist()]
467 return ("SYSTEM/recovery-from-boot.p" in namelist or
468 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700469
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700470def HasVendorPartition(target_files_zip):
471 try:
472 target_files_zip.getinfo("VENDOR/")
473 return True
474 except KeyError:
475 return False
476
Michael Runge6e836112014-04-15 17:40:21 -0700477def GetOemProperty(name, oem_props, oem_dict, info_dict):
478 if oem_props is not None and name in oem_props:
479 return oem_dict[name]
480 return GetBuildProp(name, info_dict)
481
482
483def CalculateFingerprint(oem_props, oem_dict, info_dict):
484 if oem_props is None:
485 return GetBuildProp("ro.build.fingerprint", info_dict)
486 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700487 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
488 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
489 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
490 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700491
Doug Zongkerfc44a512014-08-26 13:10:25 -0700492
Doug Zongker3c84f562014-07-31 11:06:30 -0700493def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700494 # Return an image object (suitable for passing to BlockImageDiff)
495 # for the 'which' partition (most be "system" or "vendor"). If a
496 # prebuilt image and file map are found in tmpdir they are used,
497 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700498
499 assert which in ("system", "vendor")
500
501 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700502 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
503 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700504 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700505 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700506
507 else:
508 print "building %s.img from target-files" % (which,)
509
510 # This is an 'old' target-files, which does not contain images
511 # already built. Build them.
512
Doug Zongkerfc44a512014-08-26 13:10:25 -0700513 mappath = tempfile.mkstemp()[1]
514 OPTIONS.tempfiles.append(mappath)
515
Doug Zongker3c84f562014-07-31 11:06:30 -0700516 import add_img_to_target_files
517 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700518 path = add_img_to_target_files.BuildSystem(
519 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700520 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700521 path = add_img_to_target_files.BuildVendor(
522 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700523
Tao Baoff777812015-05-12 11:42:31 -0700524 # Bug: http://b/20939131
525 # In ext4 filesystems, block 0 might be changed even being mounted
526 # R/O. We add it to clobbered_blocks so that it will be written to the
527 # target unconditionally. Note that they are still part of care_map.
528 clobbered_blocks = "0"
529
530 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700531
532
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700533def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700534 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700535 # be installed on top of. For now, we expect the API just won't
536 # change very often. Similarly for fstab, it might have changed
537 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700538 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700539
Michael Runge6e836112014-04-15 17:40:21 -0700540 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700541 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700542 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700543 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700544 if OPTIONS.oem_source is None:
545 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800546 if not OPTIONS.oem_no_mount:
547 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700548 oem_dict = common.LoadDictionaryFromLines(
549 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700550
Dan Albert8b72aef2015-03-23 19:13:21 -0700551 metadata = {
552 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700553 OPTIONS.info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700554 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
555 OPTIONS.info_dict),
556 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
557 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700558
Doug Zongker05d3dea2009-06-22 11:32:31 -0700559 device_specific = common.DeviceSpecificParams(
560 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700561 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700562 output_zip=output_zip,
563 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700564 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700565 metadata=metadata,
566 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700567
Doug Zongkerc9253822014-02-04 12:17:58 -0800568 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800569 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800570
Tao Baob4cfca52016-02-04 14:26:02 -0800571 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
572
Doug Zongker962069c2009-04-23 11:41:58 -0700573 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700574 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700575 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
576 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700577
Michael Runge6e836112014-04-15 17:40:21 -0700578 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700579 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800580
581 # Two-step package strategy (in chronological order, which is *not*
582 # the order in which the generated script has things):
583 #
584 # if stage is not "2/3" or "3/3":
585 # write recovery image to boot partition
586 # set stage to "2/3"
587 # reboot to boot partition and restart recovery
588 # else if stage is "2/3":
589 # write recovery image to recovery partition
590 # set stage to "3/3"
591 # reboot to recovery partition and restart recovery
592 # else:
593 # (stage must be "3/3")
594 # set stage to ""
595 # do normal full package installation:
596 # wipe and install system, boot image, etc.
597 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700598 # complete script normally
599 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800600
601 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
602 OPTIONS.input_tmp, "RECOVERY")
603 if OPTIONS.two_step:
604 if not OPTIONS.info_dict.get("multistage_support", None):
605 assert False, "two-step packages not supported by this build"
606 fs = OPTIONS.info_dict["fstab"]["/misc"]
607 assert fs.fs_type.upper() == "EMMC", \
608 "two-step packages only supported on devices with EMMC /misc partitions"
609 bcb_dev = {"bcb_dev": fs.device}
610 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
611 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700612if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800613""" % bcb_dev)
614 script.WriteRawImage("/recovery", "recovery.img")
615 script.AppendExtra("""
616set_stage("%(bcb_dev)s", "3/3");
617reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700618else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800619""" % bcb_dev)
620
Tao Bao6c55a8a2015-04-08 15:30:27 -0700621 # Dump fingerprints
622 script.Print("Target: %s" % CalculateFingerprint(
623 oem_props, oem_dict, OPTIONS.info_dict))
624
Doug Zongkere5ff5902012-01-17 10:55:37 -0800625 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700626
Doug Zongker01ce19c2014-02-04 13:48:15 -0800627 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700628
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700629 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800630 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700631 if HasVendorPartition(input_zip):
632 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700633
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400634 # Place a copy of file_contexts.bin into the OTA package which will be used
635 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700636 if "selinux_fc" in OPTIONS.info_dict:
637 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500638
Michael Runge7cd99ba2014-10-22 17:21:48 -0700639 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
640
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700641 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700642 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800643
Doug Zongker26e66192014-02-20 13:22:07 -0800644 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700645 # Full OTA is done as an "incremental" against an empty source
646 # image. This has the effect of writing new data from the package
647 # to the entire partition, but lets us reuse the updater code that
648 # writes incrementals to do it.
649 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
650 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700651 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700652 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800653 else:
654 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700655 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800656 if not has_recovery_patch:
657 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800658 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700659
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700660 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800661 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700662
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700663 boot_img = common.GetBootableImage(
664 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800665
Doug Zongker91a99c22014-05-09 13:15:01 -0700666 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800667 def output_sink(fn, data):
668 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700669 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800670
671 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
672 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700673
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700674 system_items.GetMetadata(input_zip)
675 system_items.Get("system").SetPermissions(script)
676
677 if HasVendorPartition(input_zip):
678 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
679 script.ShowProgress(0.1, 0)
680
681 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700682 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
683 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700684 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700685 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700686 else:
687 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700688 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700689 script.UnpackPackageDir("vendor", "/vendor")
690
691 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
692 script.MakeSymlinks(symlinks)
693
694 vendor_items.GetMetadata(input_zip)
695 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700696
Doug Zongker37974732010-09-16 17:44:38 -0700697 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700698 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700699
Doug Zongker01ce19c2014-02-04 13:48:15 -0800700 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700701 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700702
Doug Zongker01ce19c2014-02-04 13:48:15 -0800703 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700704 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700705
Doug Zongker1c390a22009-05-14 19:06:36 -0700706 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700707 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700708
Doug Zongker14833602010-02-02 13:12:04 -0800709 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800710
Doug Zongker922206e2014-03-04 13:16:24 -0800711 if OPTIONS.wipe_user_data:
712 script.ShowProgress(0.1, 10)
713 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700714
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800715 if OPTIONS.two_step:
716 script.AppendExtra("""
717set_stage("%(bcb_dev)s", "");
718""" % bcb_dev)
719 script.AppendExtra("else\n")
720 script.WriteRawImage("/boot", "recovery.img")
721 script.AppendExtra("""
722set_stage("%(bcb_dev)s", "2/3");
723reboot_now("%(bcb_dev)s", "");
724endif;
725endif;
726""" % bcb_dev)
Tao Baob4cfca52016-02-04 14:26:02 -0800727
Tao Bao4da324e2016-02-23 11:38:39 -0800728 script.SetProgress(1)
729 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -0800730 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700731 WriteMetadata(metadata, output_zip)
732
Doug Zongkerfc44a512014-08-26 13:10:25 -0700733
Dan Albert8e0178d2015-01-27 15:53:15 -0800734def WritePolicyConfig(file_name, output_zip):
735 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500736
Doug Zongker2ea21062010-04-28 16:05:21 -0700737
738def WriteMetadata(metadata, output_zip):
739 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
740 "".join(["%s=%s\n" % kv
741 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700742
Doug Zongkerfc44a512014-08-26 13:10:25 -0700743
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700744def LoadPartitionFiles(z, partition):
745 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700746 ZipFile, and return a dict of {filename: File object}."""
747 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700748 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700749 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700750 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700751 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700752 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700753 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700754 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800755 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700756
757
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700758def GetBuildProp(prop, info_dict):
759 """Return the fingerprint of the build of a given target-files info_dict."""
760 try:
761 return info_dict.get("build.prop", {})[prop]
762 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700763 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700764
Doug Zongkerfc44a512014-08-26 13:10:25 -0700765
Michael Runge4038aa82013-12-13 18:06:28 -0800766def AddToKnownPaths(filename, known_paths):
767 if filename[-1] == "/":
768 return
769 dirs = filename.split("/")[:-1]
770 while len(dirs) > 0:
771 path = "/".join(dirs)
772 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700773 break
Michael Runge4038aa82013-12-13 18:06:28 -0800774 known_paths.add(path)
775 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700776
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700777
Geremy Condra36bd3652014-02-06 19:45:10 -0800778def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700779 # TODO(tbao): We should factor out the common parts between
780 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800781 source_version = OPTIONS.source_info_dict["recovery_api_version"]
782 target_version = OPTIONS.target_info_dict["recovery_api_version"]
783
784 if source_version == 0:
785 print ("WARNING: generating edify script for a source that "
786 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700787 script = edify_generator.EdifyGenerator(
788 source_version, OPTIONS.target_info_dict,
789 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800790
Tao Bao3806c232015-07-05 21:08:33 -0700791 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
792 recovery_mount_options = OPTIONS.source_info_dict.get(
793 "recovery_mount_options")
794 oem_dict = None
795 if oem_props is not None and len(oem_props) > 0:
796 if OPTIONS.oem_source is None:
797 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800798 if not OPTIONS.oem_no_mount:
799 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700800 oem_dict = common.LoadDictionaryFromLines(
801 open(OPTIONS.oem_source).readlines())
802
Dan Albert8b72aef2015-03-23 19:13:21 -0700803 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700804 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
805 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -0800806 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700807 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800808
Tao Bao4da324e2016-02-23 11:38:39 -0800809 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
810 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
811 is_downgrade = long(post_timestamp) < long(pre_timestamp)
812
813 if OPTIONS.downgrade:
814 metadata["ota-downgrade"] = "yes"
815 if not is_downgrade:
816 raise RuntimeError("--downgrade specified but no downgrade detected: "
817 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
818 else:
819 if is_downgrade:
820 # Non-fatal here to allow generating such a package which may require
821 # manual work to adjust the post-timestamp. A legit use case is that we
822 # cut a new build C (after having A and B), but want to enfore the
823 # update path of A -> C -> B. Specifying --downgrade may not help since
824 # that would enforce a data wipe for C -> B update.
825 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
826 "The package may not be deployed properly. "
827 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
828 metadata["post-timestamp"] = post_timestamp
829
Geremy Condra36bd3652014-02-06 19:45:10 -0800830 device_specific = common.DeviceSpecificParams(
831 source_zip=source_zip,
832 source_version=source_version,
833 target_zip=target_zip,
834 target_version=target_version,
835 output_zip=output_zip,
836 script=script,
837 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700838 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800839
Tao Bao3806c232015-07-05 21:08:33 -0700840 source_fp = CalculateFingerprint(oem_props, oem_dict,
841 OPTIONS.source_info_dict)
842 target_fp = CalculateFingerprint(oem_props, oem_dict,
843 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800844 metadata["pre-build"] = source_fp
845 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700846 metadata["pre-build-incremental"] = GetBuildProp(
847 "ro.build.version.incremental", OPTIONS.source_info_dict)
848 metadata["post-build-incremental"] = GetBuildProp(
849 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800850
851 source_boot = common.GetBootableImage(
852 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
853 OPTIONS.source_info_dict)
854 target_boot = common.GetBootableImage(
855 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
856 updating_boot = (not OPTIONS.two_step and
857 (source_boot.data != target_boot.data))
858
Geremy Condra36bd3652014-02-06 19:45:10 -0800859 target_recovery = common.GetBootableImage(
860 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800861
Doug Zongkerfc44a512014-08-26 13:10:25 -0700862 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
863 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700864
865 blockimgdiff_version = 1
866 if OPTIONS.info_dict:
867 blockimgdiff_version = max(
868 int(i) for i in
869 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
870
Tianjie Xufc3422a2015-12-15 11:53:59 -0800871 # Check first block of system partition for remount R/W only if
872 # disk type is ext4
873 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
Tao Baob4cfca52016-02-04 14:26:02 -0800874 check_first_block = system_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700875 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
876 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
877 # b) the blocks listed in block map may not contain all the bytes for a given
878 # file (because they're rounded to be 4K-aligned).
879 disable_imgdiff = system_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700880 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800881 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700882 version=blockimgdiff_version,
883 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700884
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700885 if HasVendorPartition(target_zip):
886 if not HasVendorPartition(source_zip):
887 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700888 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
889 OPTIONS.source_info_dict)
890 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
891 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800892
893 # Check first block of vendor partition for remount R/W only if
894 # disk type is ext4
895 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baob4cfca52016-02-04 14:26:02 -0800896 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700897 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700898 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800899 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700900 version=blockimgdiff_version,
901 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700902 else:
903 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800904
Michael Rungec6e3afd2014-05-05 11:55:47 -0700905 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800906 device_specific.IncrementalOTA_Assertions()
907
908 # Two-step incremental package strategy (in chronological order,
909 # which is *not* the order in which the generated script has
910 # things):
911 #
912 # if stage is not "2/3" or "3/3":
913 # do verification on current system
914 # write recovery image to boot partition
915 # set stage to "2/3"
916 # reboot to boot partition and restart recovery
917 # else if stage is "2/3":
918 # write recovery image to recovery partition
919 # set stage to "3/3"
920 # reboot to recovery partition and restart recovery
921 # else:
922 # (stage must be "3/3")
923 # perform update:
924 # patch system files, etc.
925 # force full install of new boot image
926 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700927 # complete script normally
928 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800929
930 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700931 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800932 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700933 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800934 assert fs.fs_type.upper() == "EMMC", \
935 "two-step packages only supported on devices with EMMC /misc partitions"
936 bcb_dev = {"bcb_dev": fs.device}
937 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
938 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700939if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800940""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700941 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800942 script.WriteRawImage("/recovery", "recovery.img")
943 script.AppendExtra("""
944set_stage("%(bcb_dev)s", "3/3");
945reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700946else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800947""" % bcb_dev)
948
Tao Bao6c55a8a2015-04-08 15:30:27 -0700949 # Dump fingerprints
950 script.Print("Source: %s" % CalculateFingerprint(
951 oem_props, oem_dict, OPTIONS.source_info_dict))
952 script.Print("Target: %s" % CalculateFingerprint(
953 oem_props, oem_dict, OPTIONS.target_info_dict))
954
Geremy Condra36bd3652014-02-06 19:45:10 -0800955 script.Print("Verifying current system...")
956
957 device_specific.IncrementalOTA_VerifyBegin()
958
Michael Rungec6e3afd2014-05-05 11:55:47 -0700959 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700960 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
961 # patching on a device that's already on the target build will damage the
962 # system. Because operations like move don't check the block state, they
963 # always apply the changes unconditionally.
964 if blockimgdiff_version <= 2:
965 script.AssertSomeFingerprint(source_fp)
966 else:
967 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700968 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700969 if blockimgdiff_version <= 2:
970 script.AssertSomeThumbprint(
971 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
972 else:
973 script.AssertSomeThumbprint(
974 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
975 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800976
Tao Baob4cfca52016-02-04 14:26:02 -0800977 # Check the required cache size (i.e. stashed blocks).
978 size = []
979 if system_diff:
980 size.append(system_diff.required_cache)
981 if vendor_diff:
982 size.append(vendor_diff.required_cache)
983
Geremy Condra36bd3652014-02-06 19:45:10 -0800984 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700985 boot_type, boot_device = common.GetTypeAndDevice(
986 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800987 d = common.Difference(target_boot, source_boot)
988 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700989 if d is None:
990 include_full_boot = True
991 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
992 else:
993 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800994
Doug Zongkerf8340082014-08-05 10:39:37 -0700995 print "boot target: %d source: %d diff: %d" % (
996 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800997
Doug Zongkerf8340082014-08-05 10:39:37 -0700998 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800999
Doug Zongkerf8340082014-08-05 10:39:37 -07001000 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1001 (boot_type, boot_device,
1002 source_boot.size, source_boot.sha1,
1003 target_boot.size, target_boot.sha1))
Tao Baob4cfca52016-02-04 14:26:02 -08001004 size.append(target_boot.size)
1005
1006 if size:
1007 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001008
1009 device_specific.IncrementalOTA_VerifyEnd()
1010
1011 if OPTIONS.two_step:
1012 script.WriteRawImage("/boot", "recovery.img")
1013 script.AppendExtra("""
1014set_stage("%(bcb_dev)s", "2/3");
1015reboot_now("%(bcb_dev)s", "");
1016else
1017""" % bcb_dev)
1018
Jesse Zhao75bcea02015-01-06 10:59:53 -08001019 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001020 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001021 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001022 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001023
Geremy Condra36bd3652014-02-06 19:45:10 -08001024 script.Comment("---- start making changes here ----")
1025
1026 device_specific.IncrementalOTA_InstallBegin()
1027
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001028 system_diff.WriteScript(script, output_zip,
1029 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001030
Doug Zongkerfc44a512014-08-26 13:10:25 -07001031 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001032 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001033
1034 if OPTIONS.two_step:
1035 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1036 script.WriteRawImage("/boot", "boot.img")
1037 print "writing full boot image (forced by two-step mode)"
1038
1039 if not OPTIONS.two_step:
1040 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001041 if include_full_boot:
1042 print "boot image changed; including full."
1043 script.Print("Installing boot image...")
1044 script.WriteRawImage("/boot", "boot.img")
1045 else:
1046 # Produce the boot image by applying a patch to the current
1047 # contents of the boot partition, and write it back to the
1048 # partition.
1049 print "boot image changed; including patch."
1050 script.Print("Patching boot image...")
1051 script.ShowProgress(0.1, 10)
1052 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1053 % (boot_type, boot_device,
1054 source_boot.size, source_boot.sha1,
1055 target_boot.size, target_boot.sha1),
1056 "-",
1057 target_boot.size, target_boot.sha1,
1058 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001059 else:
1060 print "boot image unchanged; skipping."
1061
1062 # Do device-specific installation (eg, write radio image).
1063 device_specific.IncrementalOTA_InstallEnd()
1064
1065 if OPTIONS.extra_script is not None:
1066 script.AppendExtra(OPTIONS.extra_script)
1067
Doug Zongker922206e2014-03-04 13:16:24 -08001068 if OPTIONS.wipe_user_data:
1069 script.Print("Erasing user data...")
1070 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001071 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001072
Geremy Condra36bd3652014-02-06 19:45:10 -08001073 if OPTIONS.two_step:
1074 script.AppendExtra("""
1075set_stage("%(bcb_dev)s", "");
1076endif;
1077endif;
1078""" % bcb_dev)
1079
1080 script.SetProgress(1)
Tao Baofa41fb22016-03-08 17:53:39 -08001081 # For downgrade OTAs, we prefer to use the update-binary in the source
1082 # build that is actually newer than the one in the target build.
1083 if OPTIONS.downgrade:
1084 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1085 else:
1086 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001087 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001088 WriteMetadata(metadata, output_zip)
1089
Doug Zongker32b527d2014-03-04 10:03:02 -08001090
Tao Bao9bc6bb22015-11-09 16:58:28 -08001091def WriteVerifyPackage(input_zip, output_zip):
1092 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1093
1094 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1095 recovery_mount_options = OPTIONS.info_dict.get(
1096 "recovery_mount_options")
1097 oem_dict = None
1098 if oem_props is not None and len(oem_props) > 0:
1099 if OPTIONS.oem_source is None:
1100 raise common.ExternalError("OEM source required for this build")
1101 script.Mount("/oem", recovery_mount_options)
1102 oem_dict = common.LoadDictionaryFromLines(
1103 open(OPTIONS.oem_source).readlines())
1104
1105 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1106 metadata = {
1107 "post-build": target_fp,
1108 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1109 OPTIONS.info_dict),
1110 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1111 }
1112
1113 device_specific = common.DeviceSpecificParams(
1114 input_zip=input_zip,
1115 input_version=OPTIONS.info_dict["recovery_api_version"],
1116 output_zip=output_zip,
1117 script=script,
1118 input_tmp=OPTIONS.input_tmp,
1119 metadata=metadata,
1120 info_dict=OPTIONS.info_dict)
1121
1122 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1123
1124 script.Print("Verifying device images against %s..." % target_fp)
1125 script.AppendExtra("")
1126
1127 script.Print("Verifying boot...")
1128 boot_img = common.GetBootableImage(
1129 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1130 boot_type, boot_device = common.GetTypeAndDevice(
1131 "/boot", OPTIONS.info_dict)
1132 script.Verify("%s:%s:%d:%s" % (
1133 boot_type, boot_device, boot_img.size, boot_img.sha1))
1134 script.AppendExtra("")
1135
1136 script.Print("Verifying recovery...")
1137 recovery_img = common.GetBootableImage(
1138 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1139 recovery_type, recovery_device = common.GetTypeAndDevice(
1140 "/recovery", OPTIONS.info_dict)
1141 script.Verify("%s:%s:%d:%s" % (
1142 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1143 script.AppendExtra("")
1144
1145 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1146 system_tgt.ResetFileMap()
1147 system_diff = common.BlockDifference("system", system_tgt, src=None)
1148 system_diff.WriteStrictVerifyScript(script)
1149
1150 if HasVendorPartition(input_zip):
1151 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1152 vendor_tgt.ResetFileMap()
1153 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1154 vendor_diff.WriteStrictVerifyScript(script)
1155
1156 # Device specific partitions, such as radio, bootloader and etc.
1157 device_specific.VerifyOTA_Assertions()
1158
1159 script.SetProgress(1.0)
1160 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001161 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001162 WriteMetadata(metadata, output_zip)
1163
1164
Tao Baoc098e9e2016-01-07 13:03:56 -08001165def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1166 source_file=None):
1167 """Generate an Android OTA package that has A/B update payload."""
1168
1169 # Setup signing keys.
1170 if OPTIONS.package_key is None:
1171 OPTIONS.package_key = OPTIONS.info_dict.get(
1172 "default_system_dev_certificate",
1173 "build/target/product/security/testkey")
1174
Tao Baodea0f8b2016-06-20 17:55:06 -07001175 # A/B updater expects a signing key in RSA format. Gets the key ready for
1176 # later use in step 3, unless a payload_signer has been specified.
1177 if OPTIONS.payload_signer is None:
1178 cmd = ["openssl", "pkcs8",
1179 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1180 "-inform", "DER", "-nocrypt"]
1181 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1182 cmd.extend(["-out", rsa_key])
1183 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1184 p1.wait()
1185 assert p1.returncode == 0, "openssl pkcs8 failed"
Tao Baoc098e9e2016-01-07 13:03:56 -08001186
Tao Baodea0f8b2016-06-20 17:55:06 -07001187 # Stage the output zip package for package signing.
Tao Baoc098e9e2016-01-07 13:03:56 -08001188 temp_zip_file = tempfile.NamedTemporaryFile()
1189 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1190 compression=zipfile.ZIP_DEFLATED)
1191
1192 # Metadata to comply with Android OTA package format.
1193 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1194 oem_dict = None
1195 if oem_props:
1196 if OPTIONS.oem_source is None:
1197 raise common.ExternalError("OEM source required for this build")
1198 oem_dict = common.LoadDictionaryFromLines(
1199 open(OPTIONS.oem_source).readlines())
1200
1201 metadata = {
1202 "post-build": CalculateFingerprint(oem_props, oem_dict,
1203 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001204 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1205 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001206 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1207 OPTIONS.info_dict),
1208 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001209 "ota-required-cache": "0",
1210 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001211 }
1212
1213 if source_file is not None:
1214 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1215 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001216 metadata["pre-build-incremental"] = GetBuildProp(
1217 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001218
1219 # 1. Generate payload.
1220 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1221 cmd = ["brillo_update_payload", "generate",
1222 "--payload", payload_file,
1223 "--target_image", target_file]
1224 if source_file is not None:
1225 cmd.extend(["--source_image", source_file])
1226 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1227 p1.wait()
1228 assert p1.returncode == 0, "brillo_update_payload generate failed"
1229
1230 # 2. Generate hashes of the payload and metadata files.
1231 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1232 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1233 cmd = ["brillo_update_payload", "hash",
1234 "--unsigned_payload", payload_file,
1235 "--signature_size", "256",
1236 "--metadata_hash_file", metadata_sig_file,
1237 "--payload_hash_file", payload_sig_file]
1238 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1239 p1.wait()
1240 assert p1.returncode == 0, "brillo_update_payload hash failed"
1241
1242 # 3. Sign the hashes and insert them back into the payload file.
1243 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1244 suffix=".bin")
1245 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1246 suffix=".bin")
1247 # 3a. Sign the payload hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001248 if OPTIONS.payload_signer is not None:
1249 cmd = [OPTIONS.payload_signer,
1250 "-inkey", OPTIONS.package_key + OPTIONS.private_key_suffix]
1251 else:
1252 cmd = ["openssl", "pkeyutl", "-sign",
1253 "-inkey", rsa_key,
1254 "-pkeyopt", "digest:sha256"]
1255 cmd.extend(["-in", payload_sig_file,
1256 "-out", signed_payload_sig_file])
Tao Baoc098e9e2016-01-07 13:03:56 -08001257 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1258 p1.wait()
1259 assert p1.returncode == 0, "openssl sign payload failed"
1260
1261 # 3b. Sign the metadata hash.
Tao Baodea0f8b2016-06-20 17:55:06 -07001262 if OPTIONS.payload_signer is not None:
1263 cmd = [OPTIONS.payload_signer,
1264 "-inkey", OPTIONS.package_key + OPTIONS.private_key_suffix]
1265 else:
1266 cmd = ["openssl", "pkeyutl", "-sign",
1267 "-inkey", rsa_key,
1268 "-pkeyopt", "digest:sha256"]
1269 cmd.extend(["-in", metadata_sig_file,
1270 "-out", signed_metadata_sig_file])
Tao Baoc098e9e2016-01-07 13:03:56 -08001271 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1272 p1.wait()
1273 assert p1.returncode == 0, "openssl sign metadata failed"
1274
1275 # 3c. Insert the signatures back into the payload file.
1276 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1277 suffix=".bin")
1278 cmd = ["brillo_update_payload", "sign",
1279 "--unsigned_payload", payload_file,
1280 "--payload", signed_payload_file,
1281 "--signature_size", "256",
1282 "--metadata_signature_file", signed_metadata_sig_file,
1283 "--payload_signature_file", signed_payload_sig_file]
1284 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1285 p1.wait()
1286 assert p1.returncode == 0, "brillo_update_payload sign failed"
1287
Alex Deymo19241c12016-02-04 22:29:29 -08001288 # 4. Dump the signed payload properties.
1289 properties_file = common.MakeTempFile(prefix="payload-properties-",
1290 suffix=".txt")
1291 cmd = ["brillo_update_payload", "properties",
1292 "--payload", signed_payload_file,
1293 "--properties_file", properties_file]
1294 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1295 p1.wait()
1296 assert p1.returncode == 0, "brillo_update_payload properties failed"
1297
Tao Bao38ca0be2016-06-14 17:48:11 -07001298 if OPTIONS.wipe_user_data:
1299 with open(properties_file, "a") as f:
1300 f.write("POWERWASH=1\n")
1301 metadata["ota-wipe"] = "yes"
1302
Alex Deymo19241c12016-02-04 22:29:29 -08001303 # Add the signed payload file and properties into the zip.
1304 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001305 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1306 compress_type=zipfile.ZIP_STORED)
1307 WriteMetadata(metadata, output_zip)
1308
1309 # Sign the whole package to comply with the Android OTA package format.
1310 common.ZipClose(output_zip)
1311 SignOutput(temp_zip_file.name, output_file)
1312 temp_zip_file.close()
1313
1314
Dan Albert8b72aef2015-03-23 19:13:21 -07001315class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001316 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001317 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001318 print "Loading target..."
1319 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1320 print "Loading source..."
1321 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1322
1323 self.verbatim_targets = verbatim_targets = []
1324 self.patch_list = patch_list = []
1325 diffs = []
1326 self.renames = renames = {}
1327 known_paths = set()
1328 largest_source_size = 0
1329
1330 matching_file_cache = {}
1331 for fn, sf in source_data.items():
1332 assert fn == sf.name
1333 matching_file_cache["path:" + fn] = sf
1334 if fn in target_data.keys():
1335 AddToKnownPaths(fn, known_paths)
1336 # Only allow eligibility for filename/sha matching
1337 # if there isn't a perfect path match.
1338 if target_data.get(sf.name) is None:
1339 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1340 matching_file_cache["sha:" + sf.sha1] = sf
1341
1342 for fn in sorted(target_data.keys()):
1343 tf = target_data[fn]
1344 assert fn == tf.name
1345 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1346 if sf is not None and sf.name != tf.name:
1347 print "File has moved from " + sf.name + " to " + tf.name
1348 renames[sf.name] = tf
1349
1350 if sf is None or fn in OPTIONS.require_verbatim:
1351 # This file should be included verbatim
1352 if fn in OPTIONS.prohibit_verbatim:
1353 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1354 print "send", fn, "verbatim"
1355 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001356 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001357 if fn in target_data.keys():
1358 AddToKnownPaths(fn, known_paths)
1359 elif tf.sha1 != sf.sha1:
1360 # File is different; consider sending as a patch
1361 diffs.append(common.Difference(tf, sf))
1362 else:
1363 # Target file data identical to source (may still be renamed)
1364 pass
1365
1366 common.ComputeDifferences(diffs)
1367
1368 for diff in diffs:
1369 tf, sf, d = diff.GetPatch()
1370 path = "/".join(tf.name.split("/")[:-1])
1371 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1372 path not in known_paths:
1373 # patch is almost as big as the file; don't bother patching
1374 # or a patch + rename cannot take place due to the target
1375 # directory not existing
1376 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001377 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001378 if sf.name in renames:
1379 del renames[sf.name]
1380 AddToKnownPaths(tf.name, known_paths)
1381 else:
1382 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1383 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1384 largest_source_size = max(largest_source_size, sf.size)
1385
1386 self.largest_source_size = largest_source_size
1387
1388 def EmitVerification(self, script):
1389 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001390 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001391 if tf.name != sf.name:
1392 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1393 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1394 so_far += sf.size
1395 return so_far
1396
Michael Runge63f01de2014-10-28 19:24:19 -07001397 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001398 for fn, _, sha1 in self.verbatim_targets:
1399 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001400 script.FileCheck("/"+fn, sha1)
1401 for tf, _, _, _ in self.patch_list:
1402 script.FileCheck(tf.name, tf.sha1)
1403
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001404 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001405 file_list = ["/" + i[0] for i in self.verbatim_targets]
1406 file_list += ["/" + i for i in self.source_data
1407 if i not in self.target_data and i not in self.renames]
1408 file_list += list(extras)
1409 # Sort the list in descending order, which removes all the files first
1410 # before attempting to remove the folder. (Bug: 22960996)
1411 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001412
1413 def TotalPatchSize(self):
1414 return sum(i[1].size for i in self.patch_list)
1415
1416 def EmitPatches(self, script, total_patch_size, so_far):
1417 self.deferred_patch_list = deferred_patch_list = []
1418 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001419 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001420 if tf.name == "system/build.prop":
1421 deferred_patch_list.append(item)
1422 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001423 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001424 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001425 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1426 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001427 so_far += tf.size
1428 script.SetProgress(so_far / total_patch_size)
1429 return so_far
1430
1431 def EmitDeferredPatches(self, script):
1432 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001433 tf, sf, _, _ = item
1434 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1435 "patch/" + sf.name + ".p")
1436 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001437
1438 def EmitRenames(self, script):
1439 if len(self.renames) > 0:
1440 script.Print("Renaming files...")
1441 for src, tgt in self.renames.iteritems():
1442 print "Renaming " + src + " to " + tgt.name
1443 script.RenameFile(src, tgt.name)
1444
1445
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001446def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001447 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1448 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1449
Doug Zongker26e66192014-02-20 13:22:07 -08001450 if (OPTIONS.block_based and
1451 target_has_recovery_patch and
1452 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001453 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1454
Doug Zongker37974732010-09-16 17:44:38 -07001455 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1456 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001457
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001458 if source_version == 0:
1459 print ("WARNING: generating edify script for a source that "
1460 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001461 script = edify_generator.EdifyGenerator(
1462 source_version, OPTIONS.target_info_dict,
1463 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001464
Michael Runge6e836112014-04-15 17:40:21 -07001465 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001466 recovery_mount_options = OPTIONS.source_info_dict.get(
1467 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001468 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001469 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001470 if OPTIONS.oem_source is None:
1471 raise common.ExternalError("OEM source required for this build")
Tao Baobd25fcd2016-03-07 21:24:40 -08001472 if not OPTIONS.oem_no_mount:
1473 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001474 oem_dict = common.LoadDictionaryFromLines(
1475 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001476
Dan Albert8b72aef2015-03-23 19:13:21 -07001477 metadata = {
1478 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1479 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001480 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001481 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001482
Tao Bao4da324e2016-02-23 11:38:39 -08001483 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1484 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1485 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1486
1487 if OPTIONS.downgrade:
1488 metadata["ota-downgrade"] = "yes"
1489 if not is_downgrade:
1490 raise RuntimeError("--downgrade specified but no downgrade detected: "
1491 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1492 else:
1493 if is_downgrade:
1494 # Non-fatal here to allow generating such a package which may require
1495 # manual work to adjust the post-timestamp. A legit use case is that we
1496 # cut a new build C (after having A and B), but want to enfore the
1497 # update path of A -> C -> B. Specifying --downgrade may not help since
1498 # that would enforce a data wipe for C -> B update.
1499 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1500 "The package may not be deployed properly. "
1501 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1502 metadata["post-timestamp"] = post_timestamp
1503
Doug Zongker05d3dea2009-06-22 11:32:31 -07001504 device_specific = common.DeviceSpecificParams(
1505 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001506 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001507 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001508 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001509 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001510 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001511 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001512 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001513
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001514 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001515 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001516 if HasVendorPartition(target_zip):
1517 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001518 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001519 else:
1520 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001521
Dan Albert8b72aef2015-03-23 19:13:21 -07001522 target_fp = CalculateFingerprint(oem_props, oem_dict,
1523 OPTIONS.target_info_dict)
1524 source_fp = CalculateFingerprint(oem_props, oem_dict,
1525 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001526
1527 if oem_props is None:
1528 script.AssertSomeFingerprint(source_fp, target_fp)
1529 else:
1530 script.AssertSomeThumbprint(
1531 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1532 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1533
Doug Zongker2ea21062010-04-28 16:05:21 -07001534 metadata["pre-build"] = source_fp
1535 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001536 metadata["pre-build-incremental"] = GetBuildProp(
1537 "ro.build.version.incremental", OPTIONS.source_info_dict)
1538 metadata["post-build-incremental"] = GetBuildProp(
1539 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001540
Doug Zongker55d93282011-01-25 17:03:34 -08001541 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001542 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1543 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001544 target_boot = common.GetBootableImage(
1545 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001546 updating_boot = (not OPTIONS.two_step and
1547 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001548
Doug Zongker55d93282011-01-25 17:03:34 -08001549 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001550 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1551 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001552 target_recovery = common.GetBootableImage(
1553 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001554 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001555
Doug Zongker881dd402009-09-20 14:03:55 -07001556 # Here's how we divide up the progress bar:
1557 # 0.1 for verifying the start state (PatchCheck calls)
1558 # 0.8 for applying patches (ApplyPatch calls)
1559 # 0.1 for unpacking verbatim files, symlinking, and doing the
1560 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001561
Michael Runge6e836112014-04-15 17:40:21 -07001562 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001563 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001564
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001565 # Two-step incremental package strategy (in chronological order,
1566 # which is *not* the order in which the generated script has
1567 # things):
1568 #
1569 # if stage is not "2/3" or "3/3":
1570 # do verification on current system
1571 # write recovery image to boot partition
1572 # set stage to "2/3"
1573 # reboot to boot partition and restart recovery
1574 # else if stage is "2/3":
1575 # write recovery image to recovery partition
1576 # set stage to "3/3"
1577 # reboot to recovery partition and restart recovery
1578 # else:
1579 # (stage must be "3/3")
1580 # perform update:
1581 # patch system files, etc.
1582 # force full install of new boot image
1583 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001584 # complete script normally
1585 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001586
1587 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001588 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001589 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001590 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001591 assert fs.fs_type.upper() == "EMMC", \
1592 "two-step packages only supported on devices with EMMC /misc partitions"
1593 bcb_dev = {"bcb_dev": fs.device}
1594 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1595 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001596if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001597""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001598 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001599 script.WriteRawImage("/recovery", "recovery.img")
1600 script.AppendExtra("""
1601set_stage("%(bcb_dev)s", "3/3");
1602reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001603else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001604""" % bcb_dev)
1605
Tao Bao6c55a8a2015-04-08 15:30:27 -07001606 # Dump fingerprints
1607 script.Print("Source: %s" % (source_fp,))
1608 script.Print("Target: %s" % (target_fp,))
1609
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001610 script.Print("Verifying current system...")
1611
Doug Zongkere5ff5902012-01-17 10:55:37 -08001612 device_specific.IncrementalOTA_VerifyBegin()
1613
Doug Zongker881dd402009-09-20 14:03:55 -07001614 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001615 so_far = system_diff.EmitVerification(script)
1616 if vendor_diff:
1617 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001618
Tao Baob4cfca52016-02-04 14:26:02 -08001619 size = []
1620 if system_diff.patch_list:
1621 size.append(system_diff.largest_source_size)
1622 if vendor_diff:
1623 if vendor_diff.patch_list:
1624 size.append(vendor_diff.largest_source_size)
1625
Doug Zongker5da317e2009-06-02 13:38:17 -07001626 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001627 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001628 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001629 print "boot target: %d source: %d diff: %d" % (
1630 target_boot.size, source_boot.size, len(d))
1631
Doug Zongker048e7ca2009-06-15 14:31:53 -07001632 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001633
Tao Baodd24da92015-07-29 14:09:23 -07001634 boot_type, boot_device = common.GetTypeAndDevice(
1635 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001636
1637 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1638 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001639 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001640 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001641 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001642 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001643
Tao Baob4cfca52016-02-04 14:26:02 -08001644 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001645 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001646
Doug Zongker05d3dea2009-06-22 11:32:31 -07001647 device_specific.IncrementalOTA_VerifyEnd()
1648
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001649 if OPTIONS.two_step:
1650 script.WriteRawImage("/boot", "recovery.img")
1651 script.AppendExtra("""
1652set_stage("%(bcb_dev)s", "2/3");
1653reboot_now("%(bcb_dev)s", "");
1654else
1655""" % bcb_dev)
1656
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001657 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001658
Doug Zongkere5ff5902012-01-17 10:55:37 -08001659 device_specific.IncrementalOTA_InstallBegin()
1660
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001661 if OPTIONS.two_step:
1662 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1663 script.WriteRawImage("/boot", "boot.img")
1664 print "writing full boot image (forced by two-step mode)"
1665
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001666 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001667 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1668 if vendor_diff:
1669 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001670
Doug Zongker881dd402009-09-20 14:03:55 -07001671 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001672 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1673 if vendor_diff:
1674 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001675 if updating_boot:
1676 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001677
1678 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001679 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1680 if vendor_diff:
1681 script.Print("Patching vendor files...")
1682 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001683
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001684 if not OPTIONS.two_step:
1685 if updating_boot:
1686 # Produce the boot image by applying a patch to the current
1687 # contents of the boot partition, and write it back to the
1688 # partition.
1689 script.Print("Patching boot image...")
1690 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1691 % (boot_type, boot_device,
1692 source_boot.size, source_boot.sha1,
1693 target_boot.size, target_boot.sha1),
1694 "-",
1695 target_boot.size, target_boot.sha1,
1696 source_boot.sha1, "patch/boot.img.p")
1697 so_far += target_boot.size
1698 script.SetProgress(so_far / total_patch_size)
1699 print "boot image changed; including."
1700 else:
1701 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001702
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001703 system_items = ItemSet("system", "META/filesystem_config.txt")
1704 if vendor_diff:
1705 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1706
Doug Zongkereef39442009-04-02 12:14:19 -07001707 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001708 # Recovery is generated as a patch using both the boot image
1709 # (which contains the same linux kernel as recovery) and the file
1710 # /system/etc/recovery-resource.dat (which contains all the images
1711 # used in the recovery UI) as sources. This lets us minimize the
1712 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001713 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001714 # For older builds where recovery-resource.dat is not present, we
1715 # use only the boot image as the source.
1716
Doug Zongkerc9253822014-02-04 12:17:58 -08001717 if not target_has_recovery_patch:
1718 def output_sink(fn, data):
1719 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001720 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001721
1722 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1723 target_recovery, target_boot)
1724 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001725 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001726 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001727 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001728 else:
1729 print "recovery image unchanged; skipping."
1730
Doug Zongker881dd402009-09-20 14:03:55 -07001731 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001732
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001733 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1734 if vendor_diff:
1735 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1736
1737 temp_script = script.MakeTemporary()
1738 system_items.GetMetadata(target_zip)
1739 system_items.Get("system").SetPermissions(temp_script)
1740 if vendor_diff:
1741 vendor_items.GetMetadata(target_zip)
1742 vendor_items.Get("vendor").SetPermissions(temp_script)
1743
1744 # Note that this call will mess up the trees of Items, so make sure
1745 # we're done with them.
1746 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1747 if vendor_diff:
1748 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001749
1750 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001751 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1752
1753 # Delete all the symlinks in source that aren't in target. This
1754 # needs to happen before verbatim files are unpacked, in case a
1755 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001756
1757 # If a symlink in the source will be replaced by a regular file, we cannot
1758 # delete the symlink/file in case the package gets applied again. For such
1759 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1760 # (Bug: 23646151)
1761 replaced_symlinks = dict()
1762 if system_diff:
1763 for i in system_diff.verbatim_targets:
1764 replaced_symlinks["/%s" % (i[0],)] = i[2]
1765 if vendor_diff:
1766 for i in vendor_diff.verbatim_targets:
1767 replaced_symlinks["/%s" % (i[0],)] = i[2]
1768
1769 if system_diff:
1770 for tf in system_diff.renames.values():
1771 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1772 if vendor_diff:
1773 for tf in vendor_diff.renames.values():
1774 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1775
1776 always_delete = []
1777 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001778 for dest, link in source_symlinks:
1779 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001780 if link in replaced_symlinks:
1781 may_delete.append((link, replaced_symlinks[link]))
1782 else:
1783 always_delete.append(link)
1784 script.DeleteFiles(always_delete)
1785 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001786
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001787 if system_diff.verbatim_targets:
1788 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001789 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001790 if vendor_diff and vendor_diff.verbatim_targets:
1791 script.Print("Unpacking new vendor files...")
1792 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001793
Doug Zongkerc9253822014-02-04 12:17:58 -08001794 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001795 script.Print("Unpacking new recovery...")
1796 script.UnpackPackageDir("recovery", "/system")
1797
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001798 system_diff.EmitRenames(script)
1799 if vendor_diff:
1800 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001801
Doug Zongker05d3dea2009-06-22 11:32:31 -07001802 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001803
1804 # Create all the symlinks that don't already exist, or point to
1805 # somewhere different than what we want. Delete each symlink before
1806 # creating it, since the 'symlink' command won't overwrite.
1807 to_create = []
1808 for dest, link in target_symlinks:
1809 if link in source_symlinks_d:
1810 if dest != source_symlinks_d[link]:
1811 to_create.append((dest, link))
1812 else:
1813 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001814 script.DeleteFiles([i[1] for i in to_create])
1815 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001816
1817 # Now that the symlinks are created, we can set all the
1818 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001819 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001820
Doug Zongker881dd402009-09-20 14:03:55 -07001821 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001822 device_specific.IncrementalOTA_InstallEnd()
1823
Doug Zongker1c390a22009-05-14 19:06:36 -07001824 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001825 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001826
Doug Zongkere92f15a2011-08-26 13:46:40 -07001827 # Patch the build.prop file last, so if something fails but the
1828 # device can still come up, it appears to be the old build and will
1829 # get set the OTA package again to retry.
1830 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001831 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001832
Doug Zongker922206e2014-03-04 13:16:24 -08001833 if OPTIONS.wipe_user_data:
1834 script.Print("Erasing user data...")
1835 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001836 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001837
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001838 if OPTIONS.two_step:
1839 script.AppendExtra("""
1840set_stage("%(bcb_dev)s", "");
1841endif;
1842endif;
1843""" % bcb_dev)
1844
Michael Runge63f01de2014-10-28 19:24:19 -07001845 if OPTIONS.verify and system_diff:
1846 script.Print("Remounting and verifying system partition files...")
1847 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001848 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001849 system_diff.EmitExplicitTargetVerification(script)
1850
1851 if OPTIONS.verify and vendor_diff:
1852 script.Print("Remounting and verifying vendor partition files...")
1853 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001854 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001855 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001856
1857 # For downgrade OTAs, we prefer to use the update-binary in the source
1858 # build that is actually newer than the one in the target build.
1859 if OPTIONS.downgrade:
1860 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1861 else:
1862 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001863
Tao Baob4cfca52016-02-04 14:26:02 -08001864 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001865 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001866
1867
1868def main(argv):
1869
1870 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001871 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001872 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001873 elif o in ("-k", "--package_key"):
1874 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001875 elif o in ("-i", "--incremental_from"):
1876 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001877 elif o == "--full_radio":
1878 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001879 elif o == "--full_bootloader":
1880 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001881 elif o in ("-w", "--wipe_user_data"):
1882 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001883 elif o in ("-n", "--no_prereq"):
1884 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08001885 elif o == "--downgrade":
1886 OPTIONS.downgrade = True
1887 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001888 elif o in ("-o", "--oem_settings"):
1889 OPTIONS.oem_source = a
Tao Baodf4cb0b2016-02-25 19:49:55 -08001890 elif o == "--oem_no_mount":
1891 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001892 elif o in ("-e", "--extra_script"):
1893 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001894 elif o in ("-a", "--aslr_mode"):
1895 if a in ("on", "On", "true", "True", "yes", "Yes"):
1896 OPTIONS.aslr_mode = True
1897 else:
1898 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001899 elif o in ("-t", "--worker_threads"):
1900 if a.isdigit():
1901 OPTIONS.worker_threads = int(a)
1902 else:
1903 raise ValueError("Cannot parse value %r for option %r - only "
1904 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001905 elif o in ("-2", "--two_step"):
1906 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001907 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001908 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001909 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001910 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001911 elif o == "--block":
1912 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001913 elif o in ("-b", "--binary"):
1914 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001915 elif o in ("--no_fallback_to_full",):
1916 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001917 elif o == "--stash_threshold":
1918 try:
1919 OPTIONS.stash_threshold = float(a)
1920 except ValueError:
1921 raise ValueError("Cannot parse value %r for option %r - expecting "
1922 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001923 elif o == "--gen_verify":
1924 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001925 elif o == "--log_diff":
1926 OPTIONS.log_diff = a
Tao Baodea0f8b2016-06-20 17:55:06 -07001927 elif o == "--payload_signer":
1928 OPTIONS.payload_signer = a
Doug Zongkereef39442009-04-02 12:14:19 -07001929 else:
1930 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001931 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001932
1933 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001934 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001935 extra_long_opts=[
1936 "board_config=",
1937 "package_key=",
1938 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001939 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001940 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001941 "wipe_user_data",
1942 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08001943 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07001944 "extra_script=",
1945 "worker_threads=",
1946 "aslr_mode=",
1947 "two_step",
1948 "no_signing",
1949 "block",
1950 "binary=",
1951 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08001952 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001953 "verify",
1954 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001955 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001956 "gen_verify",
1957 "log_diff=",
Tao Baodea0f8b2016-06-20 17:55:06 -07001958 "payload_signer=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001959 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001960
1961 if len(args) != 2:
1962 common.Usage(__doc__)
1963 sys.exit(1)
1964
Tao Bao4da324e2016-02-23 11:38:39 -08001965 if OPTIONS.downgrade:
1966 # Sanity check to enforce a data wipe.
1967 if not OPTIONS.wipe_user_data:
1968 raise ValueError("Cannot downgrade without a data wipe")
1969
1970 # We should only allow downgrading incrementals (as opposed to full).
1971 # Otherwise the device may go back from arbitrary build with this full
1972 # OTA package.
1973 if OPTIONS.incremental_source is None:
1974 raise ValueError("Cannot generate downgradable full OTAs - consider"
1975 "using --omit_prereq?")
1976
Tao Baoc098e9e2016-01-07 13:03:56 -08001977 # Load the dict file from the zip directly to have a peek at the OTA type.
1978 # For packages using A/B update, unzipping is not needed.
1979 input_zip = zipfile.ZipFile(args[0], "r")
1980 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1981 common.ZipClose(input_zip)
1982
1983 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1984
1985 if ab_update:
1986 if OPTIONS.incremental_source is not None:
1987 OPTIONS.target_info_dict = OPTIONS.info_dict
1988 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1989 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1990 common.ZipClose(source_zip)
1991
1992 if OPTIONS.verbose:
1993 print "--- target info ---"
1994 common.DumpInfoDict(OPTIONS.info_dict)
1995
1996 if OPTIONS.incremental_source is not None:
1997 print "--- source info ---"
1998 common.DumpInfoDict(OPTIONS.source_info_dict)
1999
2000 WriteABOTAPackageWithBrilloScript(
2001 target_file=args[0],
2002 output_file=args[1],
2003 source_file=OPTIONS.incremental_source)
2004
2005 print "done."
2006 return
2007
Doug Zongker1c390a22009-05-14 19:06:36 -07002008 if OPTIONS.extra_script is not None:
2009 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
2010
Doug Zongkereef39442009-04-02 12:14:19 -07002011 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08002012 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07002013
Doug Zongkereef39442009-04-02 12:14:19 -07002014 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07002015 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07002016
Doug Zongker37974732010-09-16 17:44:38 -07002017 if OPTIONS.verbose:
2018 print "--- target info ---"
2019 common.DumpInfoDict(OPTIONS.info_dict)
2020
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002021 # If the caller explicitly specified the device-specific extensions
2022 # path via -s/--device_specific, use that. Otherwise, use
2023 # META/releasetools.py if it is present in the target target_files.
2024 # Otherwise, take the path of the file from 'tool_extensions' in the
2025 # info dict and look for that in the local filesystem, relative to
2026 # the current directory.
2027
Doug Zongker37974732010-09-16 17:44:38 -07002028 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002029 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2030 if os.path.exists(from_input):
2031 print "(using device-specific extensions from target_files)"
2032 OPTIONS.device_specific = from_input
2033 else:
2034 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2035
Doug Zongker37974732010-09-16 17:44:38 -07002036 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002037 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002038
Tao Baoc098e9e2016-01-07 13:03:56 -08002039 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002040 raise common.ExternalError(
2041 "--- target build has specified no recovery ---")
2042
Tao Bao767e3ac2015-11-10 12:19:19 -08002043 # Use the default key to sign the package if not specified with package_key.
2044 if not OPTIONS.no_signing:
2045 if OPTIONS.package_key is None:
2046 OPTIONS.package_key = OPTIONS.info_dict.get(
2047 "default_system_dev_certificate",
2048 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002049
Tao Bao767e3ac2015-11-10 12:19:19 -08002050 # Set up the output zip. Create a temporary zip file if signing is needed.
2051 if OPTIONS.no_signing:
2052 if os.path.exists(args[1]):
2053 os.unlink(args[1])
2054 output_zip = zipfile.ZipFile(args[1], "w",
2055 compression=zipfile.ZIP_DEFLATED)
2056 else:
2057 temp_zip_file = tempfile.NamedTemporaryFile()
2058 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2059 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002060
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002061 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002062 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002063 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002064 print "--- can't determine the cache partition size ---"
2065 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002066
Tao Bao9bc6bb22015-11-09 16:58:28 -08002067 # Generate a verify package.
2068 if OPTIONS.gen_verify:
2069 WriteVerifyPackage(input_zip, output_zip)
2070
Tao Bao767e3ac2015-11-10 12:19:19 -08002071 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002072 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002073 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002074
2075 # Generate an incremental OTA. It will fall back to generate a full OTA on
2076 # failure unless no_fallback_to_full is specified.
2077 else:
2078 print "unzipping source target-files..."
2079 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2080 OPTIONS.incremental_source)
2081 OPTIONS.target_info_dict = OPTIONS.info_dict
2082 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2083 OPTIONS.source_tmp)
2084 if OPTIONS.verbose:
2085 print "--- source info ---"
2086 common.DumpInfoDict(OPTIONS.source_info_dict)
2087 try:
2088 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002089 if OPTIONS.log_diff:
2090 out_file = open(OPTIONS.log_diff, 'w')
2091 import target_files_diff
2092 target_files_diff.recursiveDiff('',
2093 OPTIONS.source_tmp,
2094 OPTIONS.input_tmp,
2095 out_file)
2096 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002097 except ValueError:
2098 if not OPTIONS.fallback_to_full:
2099 raise
2100 print "--- failed to build incremental; falling back to full ---"
2101 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002102 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002103
Tao Bao767e3ac2015-11-10 12:19:19 -08002104 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002105
Tao Bao767e3ac2015-11-10 12:19:19 -08002106 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002107 if not OPTIONS.no_signing:
2108 SignOutput(temp_zip_file.name, args[1])
2109 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002110
Doug Zongkereef39442009-04-02 12:14:19 -07002111 print "done."
2112
2113
2114if __name__ == '__main__':
2115 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002116 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002117 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002118 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002119 print
2120 print " ERROR: %s" % (e,)
2121 print
2122 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002123 finally:
2124 common.Cleanup()