blob: 546c2511b33c10b8bb5ab0ace8907b899d3d1872 [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 Bao8608cde2016-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 Bao5d182562016-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 Bao4996cf02016-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 Bao5d182562016-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.
Doug Zongkereef39442009-04-02 12:14:19 -0700116"""
117
118import sys
119
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800120if sys.hexversion < 0x02070000:
121 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700122 sys.exit(1)
123
Doug Zongkerfc44a512014-08-26 13:10:25 -0700124import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700125import os
Tao Baoc098e9e2016-01-07 13:03:56 -0800126import subprocess
Doug Zongkereef39442009-04-02 12:14:19 -0700127import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700128import zipfile
129
130import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700131import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700132import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700133
134OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700135OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700136OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700137OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700138OPTIONS.require_verbatim = set()
139OPTIONS.prohibit_verbatim = set(("system/build.prop",))
140OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700141OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700142OPTIONS.omit_prereq = False
Tao Bao5d182562016-02-23 11:38:39 -0800143OPTIONS.downgrade = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700144OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700145OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700146OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
147if OPTIONS.worker_threads == 0:
148 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800149OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900150OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800151OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800152OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700153OPTIONS.oem_source = None
Tao Bao8608cde2016-02-25 19:49:55 -0800154OPTIONS.oem_no_mount = False
Doug Zongker62d4f182014-08-04 16:06:43 -0700155OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700156OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700157OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700158# Stash size cannot exceed cache_size * threshold.
159OPTIONS.cache_size = None
160OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800161OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800162OPTIONS.log_diff = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700163
Doug Zongkereef39442009-04-02 12:14:19 -0700164def MostPopularKey(d, default):
165 """Given a dict, return the key corresponding to the largest
166 value. Returns 'default' if the dict is empty."""
167 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700168 if not x:
169 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700170 x.sort()
171 return x[-1][1]
172
173
174def IsSymlink(info):
175 """Return true if the zipfile.ZipInfo object passed in represents a
176 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700177 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700178
Hristo Bojinov96be7202010-08-02 10:26:17 -0700179def IsRegular(info):
180 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700181 regular file."""
182 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700183
Michael Runge4038aa82013-12-13 18:06:28 -0800184def ClosestFileMatch(src, tgtfiles, existing):
185 """Returns the closest file match between a source file and list
186 of potential matches. The exact filename match is preferred,
187 then the sha1 is searched for, and finally a file with the same
188 basename is evaluated. Rename support in the updater-binary is
189 required for the latter checks to be used."""
190
191 result = tgtfiles.get("path:" + src.name)
192 if result is not None:
193 return result
194
195 if not OPTIONS.target_info_dict.get("update_rename_support", False):
196 return None
197
198 if src.size < 1000:
199 return None
200
201 result = tgtfiles.get("sha1:" + src.sha1)
202 if result is not None and existing.get(result.name) is None:
203 return result
204 result = tgtfiles.get("file:" + src.name.split("/")[-1])
205 if result is not None and existing.get(result.name) is None:
206 return result
207 return None
208
Dan Albert8b72aef2015-03-23 19:13:21 -0700209class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700210 def __init__(self, partition, fs_config):
211 self.partition = partition
212 self.fs_config = fs_config
213 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700214
Dan Albert8b72aef2015-03-23 19:13:21 -0700215 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700216 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700217 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700218 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700219
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700220 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700221 # The target_files contains a record of what the uid,
222 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700223 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700224
225 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700226 if not line:
227 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700228 columns = line.split()
229 name, uid, gid, mode = columns[:4]
230 selabel = None
231 capabilities = None
232
233 # After the first 4 columns, there are a series of key=value
234 # pairs. Extract out the fields we care about.
235 for element in columns[4:]:
236 key, value = element.split("=")
237 if key == "selabel":
238 selabel = value
239 if key == "capabilities":
240 capabilities = value
241
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700242 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700243 if i is not None:
244 i.uid = int(uid)
245 i.gid = int(gid)
246 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700247 i.selabel = selabel
248 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700249 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700250 i.children.sort(key=lambda i: i.name)
251
Tao Baof2cffbd2015-07-22 12:33:18 -0700252 # Set metadata for the files generated by this script. For full recovery
253 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700254 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700255 if i:
256 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700257 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700258 if i:
259 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700260
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700261
Dan Albert8b72aef2015-03-23 19:13:21 -0700262class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700263 """Items represent the metadata (user, group, mode) of files and
264 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700265 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700266 self.itemset = itemset
267 self.name = name
268 self.uid = None
269 self.gid = None
270 self.mode = None
271 self.selabel = None
272 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700273 self.is_dir = is_dir
274 self.descendants = None
275 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700276
277 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700278 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700279 self.parent.children.append(self)
280 else:
281 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700282 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700283 self.children = []
284
285 def Dump(self, indent=0):
286 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700287 print "%s%s %d %d %o" % (
288 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700289 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700290 print "%s%s %s %s %s" % (
291 " " * indent, self.name, self.uid, self.gid, self.mode)
292 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700293 print "%s%s" % (" "*indent, self.descendants)
294 print "%s%s" % (" "*indent, self.best_subtree)
295 for i in self.children:
296 i.Dump(indent=indent+1)
297
Doug Zongkereef39442009-04-02 12:14:19 -0700298 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700299 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700300 all children and determine the best strategy for using set_perm_recursive
301 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700302 values. Recursively calls itself for all descendants.
303
Dan Albert8b72aef2015-03-23 19:13:21 -0700304 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
305 counting up all descendants of this node. (dmode or fmode may be None.)
306 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
307 fmode, selabel, capabilities) tuple that will match the most descendants of
308 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700309 """
310
Dan Albert8b72aef2015-03-23 19:13:21 -0700311 assert self.is_dir
312 key = (self.uid, self.gid, self.mode, None, self.selabel,
313 self.capabilities)
314 self.descendants = {key: 1}
315 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700316 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700317 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700318 for k, v in i.CountChildMetadata().iteritems():
319 d[k] = d.get(k, 0) + v
320 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700321 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700322 d[k] = d.get(k, 0) + 1
323
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700324 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
325 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700326
327 # First, find the (uid, gid) pair that matches the most
328 # descendants.
329 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700330 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700331 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
332 ug = MostPopularKey(ug, (0, 0))
333
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700334 # Now find the dmode, fmode, selabel, and capabilities that match
335 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700336 best_dmode = (0, 0o755)
337 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700338 best_selabel = (0, None)
339 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700340 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700341 if k[:2] != ug:
342 continue
343 if k[2] is not None and count >= best_dmode[0]:
344 best_dmode = (count, k[2])
345 if k[3] is not None and count >= best_fmode[0]:
346 best_fmode = (count, k[3])
347 if k[4] is not None and count >= best_selabel[0]:
348 best_selabel = (count, k[4])
349 if k[5] is not None and count >= best_capabilities[0]:
350 best_capabilities = (count, k[5])
351 self.best_subtree = ug + (
352 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700353
354 return d
355
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700356 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700357 """Append set_perm/set_perm_recursive commands to 'script' to
358 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700359 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700360
361 self.CountChildMetadata()
362
363 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700364 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
365 # that the current item (and all its children) have already been set to.
366 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700367 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700368 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700369 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700370 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700371 current = item.best_subtree
372
373 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700374 item.mode != current[2] or item.selabel != current[4] or \
375 item.capabilities != current[5]:
376 script.SetPermissions("/"+item.name, item.uid, item.gid,
377 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700378
379 for i in item.children:
380 recurse(i, current)
381 else:
382 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700383 item.mode != current[3] 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
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700388 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700389
390
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700391def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
392 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700393 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800394 list of symlinks. output_zip may be None, in which case the copy is
395 skipped (but the other side effects still happen). substitute is an
396 optional dict of {output filename: contents} to be output instead of
397 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700398 """
399
400 symlinks = []
401
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700402 partition = itemset.partition
403
Doug Zongkereef39442009-04-02 12:14:19 -0700404 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700405 prefix = partition.upper() + "/"
406 if info.filename.startswith(prefix):
407 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700408 if IsSymlink(info):
409 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700410 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700411 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700412 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700413 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700414 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700415 if substitute and fn in substitute and substitute[fn] is None:
416 continue
417 if output_zip is not None:
418 if substitute and fn in substitute:
419 data = substitute[fn]
420 else:
421 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700422 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700423 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700424 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700425 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700426 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700427
428 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800429 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700430
431
Doug Zongkereef39442009-04-02 12:14:19 -0700432def SignOutput(temp_zip_name, output_zip_name):
433 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
434 pw = key_passwords[OPTIONS.package_key]
435
Doug Zongker951495f2009-08-14 12:44:19 -0700436 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
437 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700438
439
Dan Albert8b72aef2015-03-23 19:13:21 -0700440def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700441 oem_props = info_dict.get("oem_fingerprint_properties")
Tao Bao3e30d972016-03-15 13:20:19 -0700442 if not oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700443 device = GetBuildProp("ro.product.device", info_dict)
444 script.AssertDevice(device)
445 else:
446 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700447 raise common.ExternalError(
448 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700449 for prop in oem_props.split():
450 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700451 raise common.ExternalError(
452 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700453 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700454
Doug Zongkereef39442009-04-02 12:14:19 -0700455
Doug Zongkerc9253822014-02-04 12:17:58 -0800456def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700457 namelist = [name for name in target_files_zip.namelist()]
458 return ("SYSTEM/recovery-from-boot.p" in namelist or
459 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700460
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700461def HasVendorPartition(target_files_zip):
462 try:
463 target_files_zip.getinfo("VENDOR/")
464 return True
465 except KeyError:
466 return False
467
Michael Runge6e836112014-04-15 17:40:21 -0700468def GetOemProperty(name, oem_props, oem_dict, info_dict):
469 if oem_props is not None and name in oem_props:
470 return oem_dict[name]
471 return GetBuildProp(name, info_dict)
472
473
474def CalculateFingerprint(oem_props, oem_dict, info_dict):
475 if oem_props is None:
476 return GetBuildProp("ro.build.fingerprint", info_dict)
477 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700478 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
479 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
480 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
481 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700482
Doug Zongkerfc44a512014-08-26 13:10:25 -0700483
Doug Zongker3c84f562014-07-31 11:06:30 -0700484def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700485 # Return an image object (suitable for passing to BlockImageDiff)
486 # for the 'which' partition (most be "system" or "vendor"). If a
487 # prebuilt image and file map are found in tmpdir they are used,
488 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700489
490 assert which in ("system", "vendor")
491
492 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700493 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
494 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700495 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700496 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700497
498 else:
499 print "building %s.img from target-files" % (which,)
500
501 # This is an 'old' target-files, which does not contain images
502 # already built. Build them.
503
Doug Zongkerfc44a512014-08-26 13:10:25 -0700504 mappath = tempfile.mkstemp()[1]
505 OPTIONS.tempfiles.append(mappath)
506
Doug Zongker3c84f562014-07-31 11:06:30 -0700507 import add_img_to_target_files
508 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700509 path = add_img_to_target_files.BuildSystem(
510 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700511 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700512 path = add_img_to_target_files.BuildVendor(
513 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700514
Tao Baoff777812015-05-12 11:42:31 -0700515 # Bug: http://b/20939131
516 # In ext4 filesystems, block 0 might be changed even being mounted
517 # R/O. We add it to clobbered_blocks so that it will be written to the
518 # target unconditionally. Note that they are still part of care_map.
519 clobbered_blocks = "0"
520
521 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700522
523
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700524def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700525 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700526 # be installed on top of. For now, we expect the API just won't
527 # change very often. Similarly for fstab, it might have changed
528 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700529 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700530
Tao Bao838c68f2016-03-15 19:16:18 +0000531 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700532 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -0700533 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700534 if oem_props:
Michael Runge6e836112014-04-15 17:40:21 -0700535 if OPTIONS.oem_source is None:
536 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800537 if not OPTIONS.oem_no_mount:
538 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700539 oem_dict = common.LoadDictionaryFromLines(
540 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700541
Tao Bao3e30d972016-03-15 13:20:19 -0700542 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
Dan Albert8b72aef2015-03-23 19:13:21 -0700543 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700544 "post-build": target_fp,
Dan Albert8b72aef2015-03-23 19:13:21 -0700545 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
546 OPTIONS.info_dict),
547 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
548 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700549
Doug Zongker05d3dea2009-06-22 11:32:31 -0700550 device_specific = common.DeviceSpecificParams(
551 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700552 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700553 output_zip=output_zip,
554 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700555 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700556 metadata=metadata,
557 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700558
Doug Zongkerc9253822014-02-04 12:17:58 -0800559 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800560 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800561
Tao Baod8d14be2016-02-04 14:26:02 -0800562 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
563
Doug Zongker962069c2009-04-23 11:41:58 -0700564 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700565 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700566 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
567 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700568
Michael Runge6e836112014-04-15 17:40:21 -0700569 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700570 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800571
572 # Two-step package strategy (in chronological order, which is *not*
573 # the order in which the generated script has things):
574 #
575 # if stage is not "2/3" or "3/3":
576 # write recovery image to boot partition
577 # set stage to "2/3"
578 # reboot to boot partition and restart recovery
579 # else if stage is "2/3":
580 # write recovery image to recovery partition
581 # set stage to "3/3"
582 # reboot to recovery partition and restart recovery
583 # else:
584 # (stage must be "3/3")
585 # set stage to ""
586 # do normal full package installation:
587 # wipe and install system, boot image, etc.
588 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700589 # complete script normally
590 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800591
592 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
593 OPTIONS.input_tmp, "RECOVERY")
594 if OPTIONS.two_step:
595 if not OPTIONS.info_dict.get("multistage_support", None):
596 assert False, "two-step packages not supported by this build"
597 fs = OPTIONS.info_dict["fstab"]["/misc"]
598 assert fs.fs_type.upper() == "EMMC", \
599 "two-step packages only supported on devices with EMMC /misc partitions"
600 bcb_dev = {"bcb_dev": fs.device}
601 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
602 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700603if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800604""" % bcb_dev)
605 script.WriteRawImage("/recovery", "recovery.img")
606 script.AppendExtra("""
607set_stage("%(bcb_dev)s", "3/3");
608reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700609else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800610""" % bcb_dev)
611
Tao Bao6c55a8a2015-04-08 15:30:27 -0700612 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700613 script.Print("Target: %s" % target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700614
Doug Zongkere5ff5902012-01-17 10:55:37 -0800615 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700616
Doug Zongker01ce19c2014-02-04 13:48:15 -0800617 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700618
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700619 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800620 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700621 if HasVendorPartition(input_zip):
622 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700623
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400624 # Place a copy of file_contexts.bin into the OTA package which will be used
625 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700626 if "selinux_fc" in OPTIONS.info_dict:
627 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500628
Michael Runge7cd99ba2014-10-22 17:21:48 -0700629 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
630
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700631 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700632 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800633
Doug Zongker26e66192014-02-20 13:22:07 -0800634 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700635 # Full OTA is done as an "incremental" against an empty source
636 # image. This has the effect of writing new data from the package
637 # to the entire partition, but lets us reuse the updater code that
638 # writes incrementals to do it.
639 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
640 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700641 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700642 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800643 else:
644 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700645 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800646 if not has_recovery_patch:
647 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800648 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700649
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700650 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800651 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700652
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700653 boot_img = common.GetBootableImage(
654 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800655
Doug Zongker91a99c22014-05-09 13:15:01 -0700656 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800657 def output_sink(fn, data):
658 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700659 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800660
661 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
662 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700663
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700664 system_items.GetMetadata(input_zip)
665 system_items.Get("system").SetPermissions(script)
666
667 if HasVendorPartition(input_zip):
668 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
669 script.ShowProgress(0.1, 0)
670
671 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700672 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
673 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700674 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700675 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700676 else:
677 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700678 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700679 script.UnpackPackageDir("vendor", "/vendor")
680
681 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
682 script.MakeSymlinks(symlinks)
683
684 vendor_items.GetMetadata(input_zip)
685 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700686
Doug Zongker37974732010-09-16 17:44:38 -0700687 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700688 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700689
Doug Zongker01ce19c2014-02-04 13:48:15 -0800690 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700691 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700692
Doug Zongker01ce19c2014-02-04 13:48:15 -0800693 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700694 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700695
Doug Zongker1c390a22009-05-14 19:06:36 -0700696 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700697 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700698
Doug Zongker14833602010-02-02 13:12:04 -0800699 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800700
Doug Zongker922206e2014-03-04 13:16:24 -0800701 if OPTIONS.wipe_user_data:
702 script.ShowProgress(0.1, 10)
703 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700704
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800705 if OPTIONS.two_step:
706 script.AppendExtra("""
707set_stage("%(bcb_dev)s", "");
708""" % bcb_dev)
709 script.AppendExtra("else\n")
710 script.WriteRawImage("/boot", "recovery.img")
711 script.AppendExtra("""
712set_stage("%(bcb_dev)s", "2/3");
713reboot_now("%(bcb_dev)s", "");
714endif;
715endif;
716""" % bcb_dev)
Tao Baod8d14be2016-02-04 14:26:02 -0800717
Tao Bao5d182562016-02-23 11:38:39 -0800718 script.SetProgress(1)
719 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800720 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700721 WriteMetadata(metadata, output_zip)
722
Doug Zongkerfc44a512014-08-26 13:10:25 -0700723
Dan Albert8e0178d2015-01-27 15:53:15 -0800724def WritePolicyConfig(file_name, output_zip):
725 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500726
Doug Zongker2ea21062010-04-28 16:05:21 -0700727
728def WriteMetadata(metadata, output_zip):
729 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
730 "".join(["%s=%s\n" % kv
731 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700732
Doug Zongkerfc44a512014-08-26 13:10:25 -0700733
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700734def LoadPartitionFiles(z, partition):
735 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700736 ZipFile, and return a dict of {filename: File object}."""
737 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700738 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700739 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700740 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700741 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700742 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700743 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700744 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800745 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700746
747
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700748def GetBuildProp(prop, info_dict):
749 """Return the fingerprint of the build of a given target-files info_dict."""
750 try:
751 return info_dict.get("build.prop", {})[prop]
752 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700753 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700754
Doug Zongkerfc44a512014-08-26 13:10:25 -0700755
Michael Runge4038aa82013-12-13 18:06:28 -0800756def AddToKnownPaths(filename, known_paths):
757 if filename[-1] == "/":
758 return
759 dirs = filename.split("/")[:-1]
760 while len(dirs) > 0:
761 path = "/".join(dirs)
762 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700763 break
Michael Runge4038aa82013-12-13 18:06:28 -0800764 known_paths.add(path)
765 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700766
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700767
Geremy Condra36bd3652014-02-06 19:45:10 -0800768def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700769 # TODO(tbao): We should factor out the common parts between
770 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800771 source_version = OPTIONS.source_info_dict["recovery_api_version"]
772 target_version = OPTIONS.target_info_dict["recovery_api_version"]
773
774 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -0700775 print("WARNING: generating edify script for a source that "
776 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700777 script = edify_generator.EdifyGenerator(
778 source_version, OPTIONS.target_info_dict,
779 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800780
Tao Bao3806c232015-07-05 21:08:33 -0700781 recovery_mount_options = OPTIONS.source_info_dict.get(
782 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -0700783 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
784 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Tao Bao3806c232015-07-05 21:08:33 -0700785 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -0700786 if source_oem_props or target_oem_props:
Tao Bao3806c232015-07-05 21:08:33 -0700787 if OPTIONS.oem_source is None:
788 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -0800789 if not OPTIONS.oem_no_mount:
790 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700791 oem_dict = common.LoadDictionaryFromLines(
792 open(OPTIONS.oem_source).readlines())
793
Dan Albert8b72aef2015-03-23 19:13:21 -0700794 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -0700795 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
796 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800797 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700798 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800799
Tao Bao5d182562016-02-23 11:38:39 -0800800 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
801 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
802 is_downgrade = long(post_timestamp) < long(pre_timestamp)
803
804 if OPTIONS.downgrade:
805 metadata["ota-downgrade"] = "yes"
806 if not is_downgrade:
807 raise RuntimeError("--downgrade specified but no downgrade detected: "
808 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
809 else:
810 if is_downgrade:
811 # Non-fatal here to allow generating such a package which may require
812 # manual work to adjust the post-timestamp. A legit use case is that we
813 # cut a new build C (after having A and B), but want to enfore the
814 # update path of A -> C -> B. Specifying --downgrade may not help since
815 # that would enforce a data wipe for C -> B update.
816 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
817 "The package may not be deployed properly. "
818 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
819 metadata["post-timestamp"] = post_timestamp
820
Geremy Condra36bd3652014-02-06 19:45:10 -0800821 device_specific = common.DeviceSpecificParams(
822 source_zip=source_zip,
823 source_version=source_version,
824 target_zip=target_zip,
825 target_version=target_version,
826 output_zip=output_zip,
827 script=script,
828 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700829 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800830
Tao Bao3e30d972016-03-15 13:20:19 -0700831 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700832 OPTIONS.source_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -0700833 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Tao Bao3806c232015-07-05 21:08:33 -0700834 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800835 metadata["pre-build"] = source_fp
836 metadata["post-build"] = target_fp
837
838 source_boot = common.GetBootableImage(
839 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
840 OPTIONS.source_info_dict)
841 target_boot = common.GetBootableImage(
842 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
843 updating_boot = (not OPTIONS.two_step and
844 (source_boot.data != target_boot.data))
845
Geremy Condra36bd3652014-02-06 19:45:10 -0800846 target_recovery = common.GetBootableImage(
847 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800848
Doug Zongkerfc44a512014-08-26 13:10:25 -0700849 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
850 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700851
852 blockimgdiff_version = 1
853 if OPTIONS.info_dict:
854 blockimgdiff_version = max(
855 int(i) for i in
856 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
857
Tianjie Xufc3422a2015-12-15 11:53:59 -0800858 # Check first block of system partition for remount R/W only if
859 # disk type is ext4
860 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
Tao Baod8d14be2016-02-04 14:26:02 -0800861 check_first_block = system_partition.fs_type == "ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700862 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800863 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700864 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700865
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700866 if HasVendorPartition(target_zip):
867 if not HasVendorPartition(source_zip):
868 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700869 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
870 OPTIONS.source_info_dict)
871 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
872 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800873
874 # Check first block of vendor partition for remount R/W only if
875 # disk type is ext4
876 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800877 check_first_block = vendor_partition.fs_type == "ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700878 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800879 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700880 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700881 else:
882 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800883
Michael Rungec6e3afd2014-05-05 11:55:47 -0700884 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800885 device_specific.IncrementalOTA_Assertions()
886
887 # Two-step incremental package strategy (in chronological order,
888 # which is *not* the order in which the generated script has
889 # things):
890 #
891 # if stage is not "2/3" or "3/3":
892 # do verification on current system
893 # write recovery image to boot partition
894 # set stage to "2/3"
895 # reboot to boot partition and restart recovery
896 # else if stage is "2/3":
897 # write recovery image to recovery partition
898 # set stage to "3/3"
899 # reboot to recovery partition and restart recovery
900 # else:
901 # (stage must be "3/3")
902 # perform update:
903 # patch system files, etc.
904 # force full install of new boot image
905 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700906 # complete script normally
907 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800908
909 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700910 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800911 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700912 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800913 assert fs.fs_type.upper() == "EMMC", \
914 "two-step packages only supported on devices with EMMC /misc partitions"
915 bcb_dev = {"bcb_dev": fs.device}
916 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
917 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700918if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800919""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700920 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800921 script.WriteRawImage("/recovery", "recovery.img")
922 script.AppendExtra("""
923set_stage("%(bcb_dev)s", "3/3");
924reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700925else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800926""" % bcb_dev)
927
Tao Bao6c55a8a2015-04-08 15:30:27 -0700928 # Dump fingerprints
Tao Bao3e30d972016-03-15 13:20:19 -0700929 script.Print(source_fp)
930 script.Print(target_fp)
Tao Bao6c55a8a2015-04-08 15:30:27 -0700931
Geremy Condra36bd3652014-02-06 19:45:10 -0800932 script.Print("Verifying current system...")
933
934 device_specific.IncrementalOTA_VerifyBegin()
935
Tao Bao3e30d972016-03-15 13:20:19 -0700936 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
937 # patching on a device that's already on the target build will damage the
938 # system. Because operations like move don't check the block state, they
939 # always apply the changes unconditionally.
940 if blockimgdiff_version <= 2:
941 if source_oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700942 script.AssertSomeFingerprint(source_fp)
943 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700944 script.AssertSomeThumbprint(
945 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700946
947 else: # blockimgdiff_version > 2
948 if source_oem_props is None and target_oem_props is None:
949 script.AssertSomeFingerprint(source_fp, target_fp)
950 elif source_oem_props is not None and target_oem_props is not None:
Tao Baodd2a5892015-03-12 12:32:37 -0700951 script.AssertSomeThumbprint(
952 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
953 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -0700954 elif source_oem_props is None and target_oem_props is not None:
955 script.AssertFingerprintOrThumbprint(
956 source_fp,
957 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
958 else:
959 script.AssertFingerprintOrThumbprint(
960 target_fp,
961 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800962
Tao Baod8d14be2016-02-04 14:26:02 -0800963 # Check the required cache size (i.e. stashed blocks).
964 size = []
965 if system_diff:
966 size.append(system_diff.required_cache)
967 if vendor_diff:
968 size.append(vendor_diff.required_cache)
969
Geremy Condra36bd3652014-02-06 19:45:10 -0800970 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700971 boot_type, boot_device = common.GetTypeAndDevice(
972 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800973 d = common.Difference(target_boot, source_boot)
974 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700975 if d is None:
976 include_full_boot = True
977 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
978 else:
979 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800980
Doug Zongkerf8340082014-08-05 10:39:37 -0700981 print "boot target: %d source: %d diff: %d" % (
982 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800983
Doug Zongkerf8340082014-08-05 10:39:37 -0700984 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800985
Doug Zongkerf8340082014-08-05 10:39:37 -0700986 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
987 (boot_type, boot_device,
988 source_boot.size, source_boot.sha1,
989 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800990 size.append(target_boot.size)
991
992 if size:
993 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800994
995 device_specific.IncrementalOTA_VerifyEnd()
996
997 if OPTIONS.two_step:
998 script.WriteRawImage("/boot", "recovery.img")
999 script.AppendExtra("""
1000set_stage("%(bcb_dev)s", "2/3");
1001reboot_now("%(bcb_dev)s", "");
1002else
1003""" % bcb_dev)
1004
Jesse Zhao75bcea02015-01-06 10:59:53 -08001005 # Verify the existing partitions.
Tao Baod4caaae2016-04-12 15:53:16 -07001006 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001007 if vendor_diff:
Tao Baod4caaae2016-04-12 15:53:16 -07001008 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001009
Geremy Condra36bd3652014-02-06 19:45:10 -08001010 script.Comment("---- start making changes here ----")
1011
1012 device_specific.IncrementalOTA_InstallBegin()
1013
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001014 system_diff.WriteScript(script, output_zip,
1015 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001016
Doug Zongkerfc44a512014-08-26 13:10:25 -07001017 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001018 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001019
1020 if OPTIONS.two_step:
1021 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1022 script.WriteRawImage("/boot", "boot.img")
1023 print "writing full boot image (forced by two-step mode)"
1024
1025 if not OPTIONS.two_step:
1026 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001027 if include_full_boot:
1028 print "boot image changed; including full."
1029 script.Print("Installing boot image...")
1030 script.WriteRawImage("/boot", "boot.img")
1031 else:
1032 # Produce the boot image by applying a patch to the current
1033 # contents of the boot partition, and write it back to the
1034 # partition.
1035 print "boot image changed; including patch."
1036 script.Print("Patching boot image...")
1037 script.ShowProgress(0.1, 10)
1038 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1039 % (boot_type, boot_device,
1040 source_boot.size, source_boot.sha1,
1041 target_boot.size, target_boot.sha1),
1042 "-",
1043 target_boot.size, target_boot.sha1,
1044 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001045 else:
1046 print "boot image unchanged; skipping."
1047
1048 # Do device-specific installation (eg, write radio image).
1049 device_specific.IncrementalOTA_InstallEnd()
1050
1051 if OPTIONS.extra_script is not None:
1052 script.AppendExtra(OPTIONS.extra_script)
1053
Doug Zongker922206e2014-03-04 13:16:24 -08001054 if OPTIONS.wipe_user_data:
1055 script.Print("Erasing user data...")
1056 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001057 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001058
Geremy Condra36bd3652014-02-06 19:45:10 -08001059 if OPTIONS.two_step:
1060 script.AppendExtra("""
1061set_stage("%(bcb_dev)s", "");
1062endif;
1063endif;
1064""" % bcb_dev)
1065
1066 script.SetProgress(1)
Tao Bao4996cf02016-03-08 17:53:39 -08001067 # For downgrade OTAs, we prefer to use the update-binary in the source
1068 # build that is actually newer than the one in the target build.
1069 if OPTIONS.downgrade:
1070 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1071 else:
1072 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001073 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001074 WriteMetadata(metadata, output_zip)
1075
Doug Zongker32b527d2014-03-04 10:03:02 -08001076
Tao Bao9bc6bb22015-11-09 16:58:28 -08001077def WriteVerifyPackage(input_zip, output_zip):
1078 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1079
1080 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1081 recovery_mount_options = OPTIONS.info_dict.get(
1082 "recovery_mount_options")
1083 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001084 if oem_props:
Tao Bao9bc6bb22015-11-09 16:58:28 -08001085 if OPTIONS.oem_source is None:
1086 raise common.ExternalError("OEM source required for this build")
Tao Bao8608cde2016-02-25 19:49:55 -08001087 if not OPTIONS.oem_no_mount:
1088 script.Mount("/oem", recovery_mount_options)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001089 oem_dict = common.LoadDictionaryFromLines(
1090 open(OPTIONS.oem_source).readlines())
1091
1092 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1093 metadata = {
1094 "post-build": target_fp,
1095 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1096 OPTIONS.info_dict),
1097 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1098 }
1099
1100 device_specific = common.DeviceSpecificParams(
1101 input_zip=input_zip,
1102 input_version=OPTIONS.info_dict["recovery_api_version"],
1103 output_zip=output_zip,
1104 script=script,
1105 input_tmp=OPTIONS.input_tmp,
1106 metadata=metadata,
1107 info_dict=OPTIONS.info_dict)
1108
1109 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1110
1111 script.Print("Verifying device images against %s..." % target_fp)
1112 script.AppendExtra("")
1113
1114 script.Print("Verifying boot...")
1115 boot_img = common.GetBootableImage(
1116 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1117 boot_type, boot_device = common.GetTypeAndDevice(
1118 "/boot", OPTIONS.info_dict)
1119 script.Verify("%s:%s:%d:%s" % (
1120 boot_type, boot_device, boot_img.size, boot_img.sha1))
1121 script.AppendExtra("")
1122
1123 script.Print("Verifying recovery...")
1124 recovery_img = common.GetBootableImage(
1125 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1126 recovery_type, recovery_device = common.GetTypeAndDevice(
1127 "/recovery", OPTIONS.info_dict)
1128 script.Verify("%s:%s:%d:%s" % (
1129 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1130 script.AppendExtra("")
1131
1132 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1133 system_tgt.ResetFileMap()
1134 system_diff = common.BlockDifference("system", system_tgt, src=None)
1135 system_diff.WriteStrictVerifyScript(script)
1136
1137 if HasVendorPartition(input_zip):
1138 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1139 vendor_tgt.ResetFileMap()
1140 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1141 vendor_diff.WriteStrictVerifyScript(script)
1142
1143 # Device specific partitions, such as radio, bootloader and etc.
1144 device_specific.VerifyOTA_Assertions()
1145
1146 script.SetProgress(1.0)
1147 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001148 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001149 WriteMetadata(metadata, output_zip)
1150
1151
Tao Baoc098e9e2016-01-07 13:03:56 -08001152def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1153 source_file=None):
1154 """Generate an Android OTA package that has A/B update payload."""
1155
Alex Deymod8d96ec2016-06-10 16:38:31 -07001156 # The place where the output from the subprocess should go.
1157 log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
1158
Tao Baoc098e9e2016-01-07 13:03:56 -08001159 # Setup signing keys.
1160 if OPTIONS.package_key is None:
1161 OPTIONS.package_key = OPTIONS.info_dict.get(
1162 "default_system_dev_certificate",
1163 "build/target/product/security/testkey")
1164
1165 # A/B updater expects key in RSA format.
1166 cmd = ["openssl", "pkcs8",
1167 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1168 "-inform", "DER", "-nocrypt"]
1169 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1170 cmd.extend(["-out", rsa_key])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001171 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1172 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001173 assert p1.returncode == 0, "openssl pkcs8 failed"
1174
1175 # Stage the output zip package for signing.
1176 temp_zip_file = tempfile.NamedTemporaryFile()
1177 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1178 compression=zipfile.ZIP_DEFLATED)
1179
1180 # Metadata to comply with Android OTA package format.
1181 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1182 oem_dict = None
1183 if oem_props:
1184 if OPTIONS.oem_source is None:
1185 raise common.ExternalError("OEM source required for this build")
1186 oem_dict = common.LoadDictionaryFromLines(
1187 open(OPTIONS.oem_source).readlines())
1188
1189 metadata = {
1190 "post-build": CalculateFingerprint(oem_props, oem_dict,
1191 OPTIONS.info_dict),
1192 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1193 OPTIONS.info_dict),
1194 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001195 "ota-required-cache": "0",
1196 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001197 }
1198
1199 if source_file is not None:
1200 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1201 OPTIONS.source_info_dict)
1202
1203 # 1. Generate payload.
1204 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1205 cmd = ["brillo_update_payload", "generate",
1206 "--payload", payload_file,
1207 "--target_image", target_file]
1208 if source_file is not None:
1209 cmd.extend(["--source_image", source_file])
Alex Deymod8d96ec2016-06-10 16:38:31 -07001210 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1211 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001212 assert p1.returncode == 0, "brillo_update_payload generate failed"
1213
1214 # 2. Generate hashes of the payload and metadata files.
1215 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1216 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1217 cmd = ["brillo_update_payload", "hash",
1218 "--unsigned_payload", payload_file,
1219 "--signature_size", "256",
1220 "--metadata_hash_file", metadata_sig_file,
1221 "--payload_hash_file", payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001222 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1223 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001224 assert p1.returncode == 0, "brillo_update_payload hash failed"
1225
1226 # 3. Sign the hashes and insert them back into the payload file.
1227 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1228 suffix=".bin")
1229 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1230 suffix=".bin")
1231 # 3a. Sign the payload hash.
1232 cmd = ["openssl", "pkeyutl", "-sign",
1233 "-inkey", rsa_key,
1234 "-pkeyopt", "digest:sha256",
1235 "-in", payload_sig_file,
1236 "-out", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001237 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1238 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001239 assert p1.returncode == 0, "openssl sign payload failed"
1240
1241 # 3b. Sign the metadata hash.
1242 cmd = ["openssl", "pkeyutl", "-sign",
1243 "-inkey", rsa_key,
1244 "-pkeyopt", "digest:sha256",
1245 "-in", metadata_sig_file,
1246 "-out", signed_metadata_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001247 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1248 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001249 assert p1.returncode == 0, "openssl sign metadata failed"
1250
1251 # 3c. Insert the signatures back into the payload file.
1252 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1253 suffix=".bin")
1254 cmd = ["brillo_update_payload", "sign",
1255 "--unsigned_payload", payload_file,
1256 "--payload", signed_payload_file,
1257 "--signature_size", "256",
1258 "--metadata_signature_file", signed_metadata_sig_file,
1259 "--payload_signature_file", signed_payload_sig_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001260 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1261 p1.communicate()
Tao Baoc098e9e2016-01-07 13:03:56 -08001262 assert p1.returncode == 0, "brillo_update_payload sign failed"
1263
Alex Deymo19241c12016-02-04 22:29:29 -08001264 # 4. Dump the signed payload properties.
1265 properties_file = common.MakeTempFile(prefix="payload-properties-",
1266 suffix=".txt")
1267 cmd = ["brillo_update_payload", "properties",
1268 "--payload", signed_payload_file,
1269 "--properties_file", properties_file]
Alex Deymod8d96ec2016-06-10 16:38:31 -07001270 p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
1271 p1.communicate()
Alex Deymo19241c12016-02-04 22:29:29 -08001272 assert p1.returncode == 0, "brillo_update_payload properties failed"
1273
1274 # Add the signed payload file and properties into the zip.
1275 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001276 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1277 compress_type=zipfile.ZIP_STORED)
1278 WriteMetadata(metadata, output_zip)
1279
1280 # Sign the whole package to comply with the Android OTA package format.
1281 common.ZipClose(output_zip)
1282 SignOutput(temp_zip_file.name, output_file)
1283 temp_zip_file.close()
1284
1285
Dan Albert8b72aef2015-03-23 19:13:21 -07001286class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001287 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001288 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001289 print "Loading target..."
1290 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1291 print "Loading source..."
1292 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1293
1294 self.verbatim_targets = verbatim_targets = []
1295 self.patch_list = patch_list = []
1296 diffs = []
1297 self.renames = renames = {}
1298 known_paths = set()
1299 largest_source_size = 0
1300
1301 matching_file_cache = {}
1302 for fn, sf in source_data.items():
1303 assert fn == sf.name
1304 matching_file_cache["path:" + fn] = sf
1305 if fn in target_data.keys():
1306 AddToKnownPaths(fn, known_paths)
1307 # Only allow eligibility for filename/sha matching
1308 # if there isn't a perfect path match.
1309 if target_data.get(sf.name) is None:
1310 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1311 matching_file_cache["sha:" + sf.sha1] = sf
1312
1313 for fn in sorted(target_data.keys()):
1314 tf = target_data[fn]
1315 assert fn == tf.name
1316 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1317 if sf is not None and sf.name != tf.name:
1318 print "File has moved from " + sf.name + " to " + tf.name
1319 renames[sf.name] = tf
1320
1321 if sf is None or fn in OPTIONS.require_verbatim:
1322 # This file should be included verbatim
1323 if fn in OPTIONS.prohibit_verbatim:
1324 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1325 print "send", fn, "verbatim"
1326 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001327 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001328 if fn in target_data.keys():
1329 AddToKnownPaths(fn, known_paths)
1330 elif tf.sha1 != sf.sha1:
1331 # File is different; consider sending as a patch
1332 diffs.append(common.Difference(tf, sf))
1333 else:
1334 # Target file data identical to source (may still be renamed)
1335 pass
1336
1337 common.ComputeDifferences(diffs)
1338
1339 for diff in diffs:
1340 tf, sf, d = diff.GetPatch()
1341 path = "/".join(tf.name.split("/")[:-1])
1342 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1343 path not in known_paths:
1344 # patch is almost as big as the file; don't bother patching
1345 # or a patch + rename cannot take place due to the target
1346 # directory not existing
1347 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001348 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001349 if sf.name in renames:
1350 del renames[sf.name]
1351 AddToKnownPaths(tf.name, known_paths)
1352 else:
1353 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1354 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1355 largest_source_size = max(largest_source_size, sf.size)
1356
1357 self.largest_source_size = largest_source_size
1358
1359 def EmitVerification(self, script):
1360 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001361 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001362 if tf.name != sf.name:
1363 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1364 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1365 so_far += sf.size
1366 return so_far
1367
Michael Runge63f01de2014-10-28 19:24:19 -07001368 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001369 for fn, _, sha1 in self.verbatim_targets:
1370 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001371 script.FileCheck("/"+fn, sha1)
1372 for tf, _, _, _ in self.patch_list:
1373 script.FileCheck(tf.name, tf.sha1)
1374
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001375 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001376 file_list = ["/" + i[0] for i in self.verbatim_targets]
1377 file_list += ["/" + i for i in self.source_data
1378 if i not in self.target_data and i not in self.renames]
1379 file_list += list(extras)
1380 # Sort the list in descending order, which removes all the files first
1381 # before attempting to remove the folder. (Bug: 22960996)
1382 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001383
1384 def TotalPatchSize(self):
1385 return sum(i[1].size for i in self.patch_list)
1386
1387 def EmitPatches(self, script, total_patch_size, so_far):
1388 self.deferred_patch_list = deferred_patch_list = []
1389 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001390 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001391 if tf.name == "system/build.prop":
1392 deferred_patch_list.append(item)
1393 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001394 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001395 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001396 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1397 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001398 so_far += tf.size
1399 script.SetProgress(so_far / total_patch_size)
1400 return so_far
1401
1402 def EmitDeferredPatches(self, script):
1403 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001404 tf, sf, _, _ = item
1405 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1406 "patch/" + sf.name + ".p")
1407 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001408
1409 def EmitRenames(self, script):
1410 if len(self.renames) > 0:
1411 script.Print("Renaming files...")
1412 for src, tgt in self.renames.iteritems():
1413 print "Renaming " + src + " to " + tgt.name
1414 script.RenameFile(src, tgt.name)
1415
1416
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001417def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001418 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1419 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1420
Doug Zongker26e66192014-02-20 13:22:07 -08001421 if (OPTIONS.block_based and
1422 target_has_recovery_patch and
1423 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001424 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1425
Doug Zongker37974732010-09-16 17:44:38 -07001426 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1427 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001428
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001429 if source_version == 0:
Tao Bao3e30d972016-03-15 13:20:19 -07001430 print("WARNING: generating edify script for a source that "
1431 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001432 script = edify_generator.EdifyGenerator(
1433 source_version, OPTIONS.target_info_dict,
1434 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001435
Tao Bao34b47bf2015-06-22 19:17:41 -07001436 recovery_mount_options = OPTIONS.source_info_dict.get(
1437 "recovery_mount_options")
Tao Bao3e30d972016-03-15 13:20:19 -07001438 source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
1439 target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge6e836112014-04-15 17:40:21 -07001440 oem_dict = None
Tao Bao3e30d972016-03-15 13:20:19 -07001441 if source_oem_props or target_oem_props:
Michael Runge6e836112014-04-15 17:40:21 -07001442 if OPTIONS.oem_source is None:
1443 raise common.ExternalError("OEM source required for this build")
Tao Bao1bb5a182016-03-04 09:45:03 -08001444 if not OPTIONS.oem_no_mount:
1445 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001446 oem_dict = common.LoadDictionaryFromLines(
1447 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001448
Dan Albert8b72aef2015-03-23 19:13:21 -07001449 metadata = {
Tao Bao3e30d972016-03-15 13:20:19 -07001450 "pre-device": GetOemProperty("ro.product.device", source_oem_props,
1451 oem_dict, OPTIONS.source_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001452 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001453 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001454
Tao Bao5d182562016-02-23 11:38:39 -08001455 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
1456 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
1457 is_downgrade = long(post_timestamp) < long(pre_timestamp)
1458
1459 if OPTIONS.downgrade:
1460 metadata["ota-downgrade"] = "yes"
1461 if not is_downgrade:
1462 raise RuntimeError("--downgrade specified but no downgrade detected: "
1463 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
1464 else:
1465 if is_downgrade:
1466 # Non-fatal here to allow generating such a package which may require
1467 # manual work to adjust the post-timestamp. A legit use case is that we
1468 # cut a new build C (after having A and B), but want to enfore the
1469 # update path of A -> C -> B. Specifying --downgrade may not help since
1470 # that would enforce a data wipe for C -> B update.
1471 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
1472 "The package may not be deployed properly. "
1473 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
1474 metadata["post-timestamp"] = post_timestamp
1475
Doug Zongker05d3dea2009-06-22 11:32:31 -07001476 device_specific = common.DeviceSpecificParams(
1477 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001478 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001479 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001480 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001481 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001482 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001483 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001484 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001485
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001486 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001487 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001488 if HasVendorPartition(target_zip):
1489 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001490 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001491 else:
1492 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001493
Tao Bao3e30d972016-03-15 13:20:19 -07001494 target_fp = CalculateFingerprint(target_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001495 OPTIONS.target_info_dict)
Tao Bao3e30d972016-03-15 13:20:19 -07001496 source_fp = CalculateFingerprint(source_oem_props, oem_dict,
Dan Albert8b72aef2015-03-23 19:13:21 -07001497 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001498
Tao Bao3e30d972016-03-15 13:20:19 -07001499 if source_oem_props is None and target_oem_props is None:
Michael Runge6e836112014-04-15 17:40:21 -07001500 script.AssertSomeFingerprint(source_fp, target_fp)
Tao Bao3e30d972016-03-15 13:20:19 -07001501 elif source_oem_props is not None and target_oem_props is not None:
Michael Runge6e836112014-04-15 17:40:21 -07001502 script.AssertSomeThumbprint(
1503 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1504 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Tao Bao3e30d972016-03-15 13:20:19 -07001505 elif source_oem_props is None and target_oem_props is not None:
1506 script.AssertFingerprintOrThumbprint(
1507 source_fp,
1508 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
1509 else:
1510 script.AssertFingerprintOrThumbprint(
1511 target_fp,
1512 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Michael Runge6e836112014-04-15 17:40:21 -07001513
Doug Zongker2ea21062010-04-28 16:05:21 -07001514 metadata["pre-build"] = source_fp
1515 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001516
Doug Zongker55d93282011-01-25 17:03:34 -08001517 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001518 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1519 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001520 target_boot = common.GetBootableImage(
1521 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001522 updating_boot = (not OPTIONS.two_step and
1523 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001524
Doug Zongker55d93282011-01-25 17:03:34 -08001525 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001526 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1527 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001528 target_recovery = common.GetBootableImage(
1529 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001530 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001531
Doug Zongker881dd402009-09-20 14:03:55 -07001532 # Here's how we divide up the progress bar:
1533 # 0.1 for verifying the start state (PatchCheck calls)
1534 # 0.8 for applying patches (ApplyPatch calls)
1535 # 0.1 for unpacking verbatim files, symlinking, and doing the
1536 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001537
Michael Runge6e836112014-04-15 17:40:21 -07001538 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001539 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001540
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001541 # Two-step incremental package strategy (in chronological order,
1542 # which is *not* the order in which the generated script has
1543 # things):
1544 #
1545 # if stage is not "2/3" or "3/3":
1546 # do verification on current system
1547 # write recovery image to boot partition
1548 # set stage to "2/3"
1549 # reboot to boot partition and restart recovery
1550 # else if stage is "2/3":
1551 # write recovery image to recovery partition
1552 # set stage to "3/3"
1553 # reboot to recovery partition and restart recovery
1554 # else:
1555 # (stage must be "3/3")
1556 # perform update:
1557 # patch system files, etc.
1558 # force full install of new boot image
1559 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001560 # complete script normally
1561 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001562
1563 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001564 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001565 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001566 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001567 assert fs.fs_type.upper() == "EMMC", \
1568 "two-step packages only supported on devices with EMMC /misc partitions"
1569 bcb_dev = {"bcb_dev": fs.device}
1570 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1571 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001572if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001573""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001574 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001575 script.WriteRawImage("/recovery", "recovery.img")
1576 script.AppendExtra("""
1577set_stage("%(bcb_dev)s", "3/3");
1578reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001579else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001580""" % bcb_dev)
1581
Tao Bao6c55a8a2015-04-08 15:30:27 -07001582 # Dump fingerprints
1583 script.Print("Source: %s" % (source_fp,))
1584 script.Print("Target: %s" % (target_fp,))
1585
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001586 script.Print("Verifying current system...")
1587
Doug Zongkere5ff5902012-01-17 10:55:37 -08001588 device_specific.IncrementalOTA_VerifyBegin()
1589
Doug Zongker881dd402009-09-20 14:03:55 -07001590 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001591 so_far = system_diff.EmitVerification(script)
1592 if vendor_diff:
1593 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001594
Tao Baod8d14be2016-02-04 14:26:02 -08001595 size = []
1596 if system_diff.patch_list:
1597 size.append(system_diff.largest_source_size)
1598 if vendor_diff:
1599 if vendor_diff.patch_list:
1600 size.append(vendor_diff.largest_source_size)
1601
Doug Zongker5da317e2009-06-02 13:38:17 -07001602 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001603 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001604 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001605 print "boot target: %d source: %d diff: %d" % (
1606 target_boot.size, source_boot.size, len(d))
1607
Doug Zongker048e7ca2009-06-15 14:31:53 -07001608 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001609
Tao Baodd24da92015-07-29 14:09:23 -07001610 boot_type, boot_device = common.GetTypeAndDevice(
1611 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001612
1613 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1614 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001615 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001616 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001617 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001618 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001619
Tao Baod8d14be2016-02-04 14:26:02 -08001620 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001621 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001622
Doug Zongker05d3dea2009-06-22 11:32:31 -07001623 device_specific.IncrementalOTA_VerifyEnd()
1624
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001625 if OPTIONS.two_step:
1626 script.WriteRawImage("/boot", "recovery.img")
1627 script.AppendExtra("""
1628set_stage("%(bcb_dev)s", "2/3");
1629reboot_now("%(bcb_dev)s", "");
1630else
1631""" % bcb_dev)
1632
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001633 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001634
Doug Zongkere5ff5902012-01-17 10:55:37 -08001635 device_specific.IncrementalOTA_InstallBegin()
1636
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001637 if OPTIONS.two_step:
1638 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1639 script.WriteRawImage("/boot", "boot.img")
1640 print "writing full boot image (forced by two-step mode)"
1641
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001642 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001643 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1644 if vendor_diff:
1645 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001646
Doug Zongker881dd402009-09-20 14:03:55 -07001647 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001648 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1649 if vendor_diff:
1650 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001651 if updating_boot:
1652 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001653
1654 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001655 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1656 if vendor_diff:
1657 script.Print("Patching vendor files...")
1658 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001659
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001660 if not OPTIONS.two_step:
1661 if updating_boot:
1662 # Produce the boot image by applying a patch to the current
1663 # contents of the boot partition, and write it back to the
1664 # partition.
1665 script.Print("Patching boot image...")
1666 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1667 % (boot_type, boot_device,
1668 source_boot.size, source_boot.sha1,
1669 target_boot.size, target_boot.sha1),
1670 "-",
1671 target_boot.size, target_boot.sha1,
1672 source_boot.sha1, "patch/boot.img.p")
1673 so_far += target_boot.size
1674 script.SetProgress(so_far / total_patch_size)
1675 print "boot image changed; including."
1676 else:
1677 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001678
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001679 system_items = ItemSet("system", "META/filesystem_config.txt")
1680 if vendor_diff:
1681 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1682
Doug Zongkereef39442009-04-02 12:14:19 -07001683 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001684 # Recovery is generated as a patch using both the boot image
1685 # (which contains the same linux kernel as recovery) and the file
1686 # /system/etc/recovery-resource.dat (which contains all the images
1687 # used in the recovery UI) as sources. This lets us minimize the
1688 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001689 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001690 # For older builds where recovery-resource.dat is not present, we
1691 # use only the boot image as the source.
1692
Doug Zongkerc9253822014-02-04 12:17:58 -08001693 if not target_has_recovery_patch:
1694 def output_sink(fn, data):
1695 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001696 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001697
1698 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1699 target_recovery, target_boot)
1700 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001701 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001702 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001703 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001704 else:
1705 print "recovery image unchanged; skipping."
1706
Doug Zongker881dd402009-09-20 14:03:55 -07001707 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001708
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001709 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1710 if vendor_diff:
1711 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1712
1713 temp_script = script.MakeTemporary()
1714 system_items.GetMetadata(target_zip)
1715 system_items.Get("system").SetPermissions(temp_script)
1716 if vendor_diff:
1717 vendor_items.GetMetadata(target_zip)
1718 vendor_items.Get("vendor").SetPermissions(temp_script)
1719
1720 # Note that this call will mess up the trees of Items, so make sure
1721 # we're done with them.
1722 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1723 if vendor_diff:
1724 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001725
1726 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001727 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1728
1729 # Delete all the symlinks in source that aren't in target. This
1730 # needs to happen before verbatim files are unpacked, in case a
1731 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001732
1733 # If a symlink in the source will be replaced by a regular file, we cannot
1734 # delete the symlink/file in case the package gets applied again. For such
1735 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1736 # (Bug: 23646151)
1737 replaced_symlinks = dict()
1738 if system_diff:
1739 for i in system_diff.verbatim_targets:
1740 replaced_symlinks["/%s" % (i[0],)] = i[2]
1741 if vendor_diff:
1742 for i in vendor_diff.verbatim_targets:
1743 replaced_symlinks["/%s" % (i[0],)] = i[2]
1744
1745 if system_diff:
1746 for tf in system_diff.renames.values():
1747 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1748 if vendor_diff:
1749 for tf in vendor_diff.renames.values():
1750 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1751
1752 always_delete = []
1753 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001754 for dest, link in source_symlinks:
1755 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001756 if link in replaced_symlinks:
1757 may_delete.append((link, replaced_symlinks[link]))
1758 else:
1759 always_delete.append(link)
1760 script.DeleteFiles(always_delete)
1761 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001762
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001763 if system_diff.verbatim_targets:
1764 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001765 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001766 if vendor_diff and vendor_diff.verbatim_targets:
1767 script.Print("Unpacking new vendor files...")
1768 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001769
Doug Zongkerc9253822014-02-04 12:17:58 -08001770 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001771 script.Print("Unpacking new recovery...")
1772 script.UnpackPackageDir("recovery", "/system")
1773
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001774 system_diff.EmitRenames(script)
1775 if vendor_diff:
1776 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001777
Doug Zongker05d3dea2009-06-22 11:32:31 -07001778 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001779
1780 # Create all the symlinks that don't already exist, or point to
1781 # somewhere different than what we want. Delete each symlink before
1782 # creating it, since the 'symlink' command won't overwrite.
1783 to_create = []
1784 for dest, link in target_symlinks:
1785 if link in source_symlinks_d:
1786 if dest != source_symlinks_d[link]:
1787 to_create.append((dest, link))
1788 else:
1789 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001790 script.DeleteFiles([i[1] for i in to_create])
1791 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001792
1793 # Now that the symlinks are created, we can set all the
1794 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001795 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001796
Doug Zongker881dd402009-09-20 14:03:55 -07001797 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001798 device_specific.IncrementalOTA_InstallEnd()
1799
Doug Zongker1c390a22009-05-14 19:06:36 -07001800 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001801 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001802
Doug Zongkere92f15a2011-08-26 13:46:40 -07001803 # Patch the build.prop file last, so if something fails but the
1804 # device can still come up, it appears to be the old build and will
1805 # get set the OTA package again to retry.
1806 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001807 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001808
Doug Zongker922206e2014-03-04 13:16:24 -08001809 if OPTIONS.wipe_user_data:
1810 script.Print("Erasing user data...")
1811 script.FormatPartition("/data")
Tao Bao5d182562016-02-23 11:38:39 -08001812 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001813
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001814 if OPTIONS.two_step:
1815 script.AppendExtra("""
1816set_stage("%(bcb_dev)s", "");
1817endif;
1818endif;
1819""" % bcb_dev)
1820
Michael Runge63f01de2014-10-28 19:24:19 -07001821 if OPTIONS.verify and system_diff:
1822 script.Print("Remounting and verifying system partition files...")
1823 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001824 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001825 system_diff.EmitExplicitTargetVerification(script)
1826
1827 if OPTIONS.verify and vendor_diff:
1828 script.Print("Remounting and verifying vendor partition files...")
1829 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001830 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001831 vendor_diff.EmitExplicitTargetVerification(script)
Tao Bao4996cf02016-03-08 17:53:39 -08001832
1833 # For downgrade OTAs, we prefer to use the update-binary in the source
1834 # build that is actually newer than the one in the target build.
1835 if OPTIONS.downgrade:
1836 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1837 else:
1838 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001839
Tao Baod8d14be2016-02-04 14:26:02 -08001840 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001841 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001842
1843
1844def main(argv):
1845
1846 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001847 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001848 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001849 elif o in ("-k", "--package_key"):
1850 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001851 elif o in ("-i", "--incremental_from"):
1852 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001853 elif o == "--full_radio":
1854 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001855 elif o == "--full_bootloader":
1856 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001857 elif o in ("-w", "--wipe_user_data"):
1858 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001859 elif o in ("-n", "--no_prereq"):
1860 OPTIONS.omit_prereq = True
Tao Bao5d182562016-02-23 11:38:39 -08001861 elif o == "--downgrade":
1862 OPTIONS.downgrade = True
1863 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001864 elif o in ("-o", "--oem_settings"):
1865 OPTIONS.oem_source = a
Tao Bao8608cde2016-02-25 19:49:55 -08001866 elif o == "--oem_no_mount":
1867 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001868 elif o in ("-e", "--extra_script"):
1869 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001870 elif o in ("-a", "--aslr_mode"):
1871 if a in ("on", "On", "true", "True", "yes", "Yes"):
1872 OPTIONS.aslr_mode = True
1873 else:
1874 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001875 elif o in ("-t", "--worker_threads"):
1876 if a.isdigit():
1877 OPTIONS.worker_threads = int(a)
1878 else:
1879 raise ValueError("Cannot parse value %r for option %r - only "
1880 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001881 elif o in ("-2", "--two_step"):
1882 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001883 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001884 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001885 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001886 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001887 elif o == "--block":
1888 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001889 elif o in ("-b", "--binary"):
1890 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001891 elif o in ("--no_fallback_to_full",):
1892 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001893 elif o == "--stash_threshold":
1894 try:
1895 OPTIONS.stash_threshold = float(a)
1896 except ValueError:
1897 raise ValueError("Cannot parse value %r for option %r - expecting "
1898 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001899 elif o == "--gen_verify":
1900 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001901 elif o == "--log_diff":
1902 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001903 else:
1904 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001905 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001906
1907 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001908 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001909 extra_long_opts=[
1910 "board_config=",
1911 "package_key=",
1912 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001913 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001914 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001915 "wipe_user_data",
1916 "no_prereq",
Tao Bao5d182562016-02-23 11:38:39 -08001917 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07001918 "extra_script=",
1919 "worker_threads=",
1920 "aslr_mode=",
1921 "two_step",
1922 "no_signing",
1923 "block",
1924 "binary=",
1925 "oem_settings=",
Tao Bao8608cde2016-02-25 19:49:55 -08001926 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001927 "verify",
1928 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001929 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001930 "gen_verify",
1931 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001932 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001933
1934 if len(args) != 2:
1935 common.Usage(__doc__)
1936 sys.exit(1)
1937
Tao Bao5d182562016-02-23 11:38:39 -08001938 if OPTIONS.downgrade:
1939 # Sanity check to enforce a data wipe.
1940 if not OPTIONS.wipe_user_data:
1941 raise ValueError("Cannot downgrade without a data wipe")
1942
1943 # We should only allow downgrading incrementals (as opposed to full).
1944 # Otherwise the device may go back from arbitrary build with this full
1945 # OTA package.
1946 if OPTIONS.incremental_source is None:
1947 raise ValueError("Cannot generate downgradable full OTAs - consider"
1948 "using --omit_prereq?")
1949
Tao Baoc098e9e2016-01-07 13:03:56 -08001950 # Load the dict file from the zip directly to have a peek at the OTA type.
1951 # For packages using A/B update, unzipping is not needed.
1952 input_zip = zipfile.ZipFile(args[0], "r")
1953 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1954 common.ZipClose(input_zip)
1955
1956 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1957
1958 if ab_update:
1959 if OPTIONS.incremental_source is not None:
1960 OPTIONS.target_info_dict = OPTIONS.info_dict
1961 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1962 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1963 common.ZipClose(source_zip)
1964
1965 if OPTIONS.verbose:
1966 print "--- target info ---"
1967 common.DumpInfoDict(OPTIONS.info_dict)
1968
1969 if OPTIONS.incremental_source is not None:
1970 print "--- source info ---"
1971 common.DumpInfoDict(OPTIONS.source_info_dict)
1972
1973 WriteABOTAPackageWithBrilloScript(
1974 target_file=args[0],
1975 output_file=args[1],
1976 source_file=OPTIONS.incremental_source)
1977
1978 print "done."
1979 return
1980
Doug Zongker1c390a22009-05-14 19:06:36 -07001981 if OPTIONS.extra_script is not None:
1982 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1983
Doug Zongkereef39442009-04-02 12:14:19 -07001984 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001985 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001986
Doug Zongkereef39442009-04-02 12:14:19 -07001987 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001988 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001989
Doug Zongker37974732010-09-16 17:44:38 -07001990 if OPTIONS.verbose:
1991 print "--- target info ---"
1992 common.DumpInfoDict(OPTIONS.info_dict)
1993
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001994 # If the caller explicitly specified the device-specific extensions
1995 # path via -s/--device_specific, use that. Otherwise, use
1996 # META/releasetools.py if it is present in the target target_files.
1997 # Otherwise, take the path of the file from 'tool_extensions' in the
1998 # info dict and look for that in the local filesystem, relative to
1999 # the current directory.
2000
Doug Zongker37974732010-09-16 17:44:38 -07002001 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002002 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
2003 if os.path.exists(from_input):
2004 print "(using device-specific extensions from target_files)"
2005 OPTIONS.device_specific = from_input
2006 else:
2007 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2008
Doug Zongker37974732010-09-16 17:44:38 -07002009 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002010 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002011
Tao Baoc098e9e2016-01-07 13:03:56 -08002012 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002013 raise common.ExternalError(
2014 "--- target build has specified no recovery ---")
2015
Tao Bao767e3ac2015-11-10 12:19:19 -08002016 # Use the default key to sign the package if not specified with package_key.
2017 if not OPTIONS.no_signing:
2018 if OPTIONS.package_key is None:
2019 OPTIONS.package_key = OPTIONS.info_dict.get(
2020 "default_system_dev_certificate",
2021 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002022
Tao Bao767e3ac2015-11-10 12:19:19 -08002023 # Set up the output zip. Create a temporary zip file if signing is needed.
2024 if OPTIONS.no_signing:
2025 if os.path.exists(args[1]):
2026 os.unlink(args[1])
2027 output_zip = zipfile.ZipFile(args[1], "w",
2028 compression=zipfile.ZIP_DEFLATED)
2029 else:
2030 temp_zip_file = tempfile.NamedTemporaryFile()
2031 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2032 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002033
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002034 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002035 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002036 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002037 print "--- can't determine the cache partition size ---"
2038 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002039
Tao Bao9bc6bb22015-11-09 16:58:28 -08002040 # Generate a verify package.
2041 if OPTIONS.gen_verify:
2042 WriteVerifyPackage(input_zip, output_zip)
2043
Tao Bao767e3ac2015-11-10 12:19:19 -08002044 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002045 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002046 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002047
2048 # Generate an incremental OTA. It will fall back to generate a full OTA on
2049 # failure unless no_fallback_to_full is specified.
2050 else:
2051 print "unzipping source target-files..."
2052 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2053 OPTIONS.incremental_source)
2054 OPTIONS.target_info_dict = OPTIONS.info_dict
2055 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2056 OPTIONS.source_tmp)
2057 if OPTIONS.verbose:
2058 print "--- source info ---"
2059 common.DumpInfoDict(OPTIONS.source_info_dict)
2060 try:
2061 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002062 if OPTIONS.log_diff:
2063 out_file = open(OPTIONS.log_diff, 'w')
2064 import target_files_diff
2065 target_files_diff.recursiveDiff('',
2066 OPTIONS.source_tmp,
2067 OPTIONS.input_tmp,
2068 out_file)
2069 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002070 except ValueError:
2071 if not OPTIONS.fallback_to_full:
2072 raise
2073 print "--- failed to build incremental; falling back to full ---"
2074 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002075 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002076
Tao Bao767e3ac2015-11-10 12:19:19 -08002077 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002078
Tao Bao767e3ac2015-11-10 12:19:19 -08002079 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002080 if not OPTIONS.no_signing:
2081 SignOutput(temp_zip_file.name, args[1])
2082 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002083
Doug Zongkereef39442009-04-02 12:14:19 -07002084 print "done."
2085
2086
2087if __name__ == '__main__':
2088 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002089 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002090 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002091 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002092 print
2093 print " ERROR: %s" % (e,)
2094 print
2095 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002096 finally:
2097 common.Cleanup()