blob: 3224305440ef18f50eb851bc022f0e7c0dce3164 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
Doug Zongker25568482014-03-03 10:21:27 -080024 --board_config <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Tao Bao43078aa2015-04-21 14:32:35 -070040 --full_radio
41 When generating an incremental OTA, always include a full copy of
42 radio image. This option is only meaningful when -i is specified,
43 because a full radio is always included in a full OTA if applicable.
44
leozwangaa6c1a12015-08-14 10:57:58 -070045 --full_bootloader
46 Similar to --full_radio. When generating an incremental OTA, always
47 include a full copy of bootloader image.
48
Michael Runge63f01de2014-10-28 19:24:19 -070049 -v (--verify)
50 Remount and verify the checksums of the files written to the
51 system and vendor (if used) partitions. Incremental builds only.
52
Michael Runge6e836112014-04-15 17:40:21 -070053 -o (--oem_settings) <file>
54 Use the file to specify the expected OEM-specific properties
55 on the OEM partition of the intended device.
56
Tao Baodf4cb0b2016-02-25 19:49:55 -080057 --oem_no_mount
58 For devices with OEM-specific properties but without an OEM partition,
59 do not mount the OEM partition in the updater-script. This should be
60 very rarely used, since it's expected to have a dedicated OEM partition
61 for OEM-specific properties. Only meaningful when -o is specified.
62
Doug Zongkerdbfaae52009-04-21 17:12:54 -070063 -w (--wipe_user_data)
64 Generate an OTA package that will wipe the user data partition
65 when installed.
66
Doug Zongker962069c2009-04-23 11:41:58 -070067 -n (--no_prereq)
68 Omit the timestamp prereq check normally included at the top of
69 the build scripts (used for developer OTA packages which
70 legitimately need to go back and forth).
71
Tao Bao4da324e2016-02-23 11:38:39 -080072 --downgrade
73 Intentionally generate an incremental OTA that updates from a newer
74 build to an older one (based on timestamp comparison). "post-timestamp"
75 will be replaced by "ota-downgrade=yes" in the metadata file. A data
76 wipe will always be enforced, so "ota-wipe=yes" will also be included in
Tao Baofa41fb22016-03-08 17:53:39 -080077 the metadata file. The update-binary in the source build will be used in
78 the OTA package, unless --binary flag is specified.
Tao Bao4da324e2016-02-23 11:38:39 -080079
Doug Zongker1c390a22009-05-14 19:06:36 -070080 -e (--extra_script) <file>
81 Insert the contents of file at the end of the update script.
82
Hristo Bojinovdafb0422010-08-26 14:35:16 -070083 -a (--aslr_mode) <on|off>
84 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050085
Doug Zongker9b23f2c2013-11-25 14:44:12 -080086 -2 (--two_step)
87 Generate a 'two-step' OTA package, where recovery is updated
88 first, so that any changes made to the system partition are done
89 using the new recovery (new kernel, etc.).
90
Doug Zongker26e66192014-02-20 13:22:07 -080091 --block
92 Generate a block-based OTA if possible. Will fall back to a
93 file-based OTA if the target_files is older and doesn't support
94 block-based OTAs.
95
Doug Zongker25568482014-03-03 10:21:27 -080096 -b (--binary) <file>
97 Use the given binary as the update-binary in the output package,
98 instead of the binary in the build's target_files. Use for
99 development only.
100
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200101 -t (--worker_threads) <int>
102 Specifies the number of worker-threads that will be used when
103 generating patches for incremental updates (defaults to 3).
104
Tao Bao8dcf7382015-05-21 14:09:49 -0700105 --stash_threshold <float>
106 Specifies the threshold that will be used to compute the maximum
107 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -0800108
109 --gen_verify
110 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -0800111
112 --log_diff <file>
113 Generate a log file that shows the differences in the source and target
114 builds for an incremental package. This option is only meaningful when
115 -i is specified.
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 Bao4da324e2016-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 Baodf4cb0b2016-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")
Michael Runge560569a2014-09-18 15:12:45 -0700442 if oem_props is None or len(oem_props) == 0:
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
Michael Runge6e836112014-04-15 17:40:21 -0700531 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700532 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700533 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700534 if oem_props is not None and len(oem_props) > 0:
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 Baodf4cb0b2016-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
Dan Albert8b72aef2015-03-23 19:13:21 -0700542 metadata = {
543 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700544 OPTIONS.info_dict),
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 Baob4cfca52016-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
613 script.Print("Target: %s" % CalculateFingerprint(
614 oem_props, oem_dict, OPTIONS.info_dict))
615
Doug Zongkere5ff5902012-01-17 10:55:37 -0800616 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700617
Doug Zongker01ce19c2014-02-04 13:48:15 -0800618 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700619
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700620 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800621 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700622 if HasVendorPartition(input_zip):
623 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700624
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400625 # Place a copy of file_contexts.bin into the OTA package which will be used
626 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700627 if "selinux_fc" in OPTIONS.info_dict:
628 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500629
Michael Runge7cd99ba2014-10-22 17:21:48 -0700630 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
631
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700632 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700633 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800634
Doug Zongker26e66192014-02-20 13:22:07 -0800635 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700636 # Full OTA is done as an "incremental" against an empty source
637 # image. This has the effect of writing new data from the package
638 # to the entire partition, but lets us reuse the updater code that
639 # writes incrementals to do it.
640 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
641 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700642 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700643 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800644 else:
645 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700646 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800647 if not has_recovery_patch:
648 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800649 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700650
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700651 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800652 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700653
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700654 boot_img = common.GetBootableImage(
655 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800656
Doug Zongker91a99c22014-05-09 13:15:01 -0700657 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800658 def output_sink(fn, data):
659 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700660 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800661
662 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
663 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700664
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700665 system_items.GetMetadata(input_zip)
666 system_items.Get("system").SetPermissions(script)
667
668 if HasVendorPartition(input_zip):
669 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
670 script.ShowProgress(0.1, 0)
671
672 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700673 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
674 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700675 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700676 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700677 else:
678 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700679 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700680 script.UnpackPackageDir("vendor", "/vendor")
681
682 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
683 script.MakeSymlinks(symlinks)
684
685 vendor_items.GetMetadata(input_zip)
686 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700687
Doug Zongker37974732010-09-16 17:44:38 -0700688 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700689 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700690
Doug Zongker01ce19c2014-02-04 13:48:15 -0800691 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700692 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700693
Doug Zongker01ce19c2014-02-04 13:48:15 -0800694 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700695 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700696
Doug Zongker1c390a22009-05-14 19:06:36 -0700697 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700698 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700699
Doug Zongker14833602010-02-02 13:12:04 -0800700 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800701
Doug Zongker922206e2014-03-04 13:16:24 -0800702 if OPTIONS.wipe_user_data:
703 script.ShowProgress(0.1, 10)
704 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700705
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800706 if OPTIONS.two_step:
707 script.AppendExtra("""
708set_stage("%(bcb_dev)s", "");
709""" % bcb_dev)
710 script.AppendExtra("else\n")
711 script.WriteRawImage("/boot", "recovery.img")
712 script.AppendExtra("""
713set_stage("%(bcb_dev)s", "2/3");
714reboot_now("%(bcb_dev)s", "");
715endif;
716endif;
717""" % bcb_dev)
Tao Baob4cfca52016-02-04 14:26:02 -0800718
Tao Bao4da324e2016-02-23 11:38:39 -0800719 script.SetProgress(1)
720 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -0800721 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700722 WriteMetadata(metadata, output_zip)
723
Doug Zongkerfc44a512014-08-26 13:10:25 -0700724
Dan Albert8e0178d2015-01-27 15:53:15 -0800725def WritePolicyConfig(file_name, output_zip):
726 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500727
Doug Zongker2ea21062010-04-28 16:05:21 -0700728
729def WriteMetadata(metadata, output_zip):
730 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
731 "".join(["%s=%s\n" % kv
732 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700733
Doug Zongkerfc44a512014-08-26 13:10:25 -0700734
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700735def LoadPartitionFiles(z, partition):
736 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700737 ZipFile, and return a dict of {filename: File object}."""
738 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700739 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700740 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700741 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700742 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700743 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700744 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700745 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800746 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700747
748
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700749def GetBuildProp(prop, info_dict):
750 """Return the fingerprint of the build of a given target-files info_dict."""
751 try:
752 return info_dict.get("build.prop", {})[prop]
753 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700754 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700755
Doug Zongkerfc44a512014-08-26 13:10:25 -0700756
Michael Runge4038aa82013-12-13 18:06:28 -0800757def AddToKnownPaths(filename, known_paths):
758 if filename[-1] == "/":
759 return
760 dirs = filename.split("/")[:-1]
761 while len(dirs) > 0:
762 path = "/".join(dirs)
763 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700764 break
Michael Runge4038aa82013-12-13 18:06:28 -0800765 known_paths.add(path)
766 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700767
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700768
Tao Bao99c17252017-02-07 11:21:17 -0800769def HandleDowngradeMetadata(metadata):
770 # Only incremental OTAs are allowed to reach here.
771 assert OPTIONS.incremental_source is not None
772
773 post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
774 pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
775 is_downgrade = long(post_timestamp) < long(pre_timestamp)
776
777 if OPTIONS.downgrade:
778 metadata["ota-downgrade"] = "yes"
779 if not is_downgrade:
780 raise RuntimeError("--downgrade specified but no downgrade detected: "
781 "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
782 else:
783 if is_downgrade:
784 # Non-fatal here to allow generating such a package which may require
785 # manual work to adjust the post-timestamp. A legit use case is that we
786 # cut a new build C (after having A and B), but want to enfore the
787 # update path of A -> C -> B. Specifying --downgrade may not help since
788 # that would enforce a data wipe for C -> B update.
789 print("\nWARNING: downgrade detected: pre: %s, post: %s.\n"
790 "The package may not be deployed properly. "
791 "Try --downgrade?\n" % (pre_timestamp, post_timestamp))
792 metadata["post-timestamp"] = post_timestamp
793
794
Geremy Condra36bd3652014-02-06 19:45:10 -0800795def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700796 # TODO(tbao): We should factor out the common parts between
797 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800798 source_version = OPTIONS.source_info_dict["recovery_api_version"]
799 target_version = OPTIONS.target_info_dict["recovery_api_version"]
800
801 if source_version == 0:
802 print ("WARNING: generating edify script for a source that "
803 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700804 script = edify_generator.EdifyGenerator(
805 source_version, OPTIONS.target_info_dict,
806 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800807
Tao Bao3806c232015-07-05 21:08:33 -0700808 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
809 recovery_mount_options = OPTIONS.source_info_dict.get(
810 "recovery_mount_options")
811 oem_dict = None
812 if oem_props is not None and len(oem_props) > 0:
813 if OPTIONS.oem_source is None:
814 raise common.ExternalError("OEM source required for this build")
Tao Baodf4cb0b2016-02-25 19:49:55 -0800815 if not OPTIONS.oem_no_mount:
816 script.Mount("/oem", recovery_mount_options)
Tao Bao3806c232015-07-05 21:08:33 -0700817 oem_dict = common.LoadDictionaryFromLines(
818 open(OPTIONS.oem_source).readlines())
819
Dan Albert8b72aef2015-03-23 19:13:21 -0700820 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700821 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
822 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -0800823 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700824 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800825
Tao Bao99c17252017-02-07 11:21:17 -0800826 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -0800827
Geremy Condra36bd3652014-02-06 19:45:10 -0800828 device_specific = common.DeviceSpecificParams(
829 source_zip=source_zip,
830 source_version=source_version,
831 target_zip=target_zip,
832 target_version=target_version,
833 output_zip=output_zip,
834 script=script,
835 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700836 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800837
Tao Bao3806c232015-07-05 21:08:33 -0700838 source_fp = CalculateFingerprint(oem_props, oem_dict,
839 OPTIONS.source_info_dict)
840 target_fp = CalculateFingerprint(oem_props, oem_dict,
841 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800842 metadata["pre-build"] = source_fp
843 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -0700844 metadata["pre-build-incremental"] = GetBuildProp(
845 "ro.build.version.incremental", OPTIONS.source_info_dict)
846 metadata["post-build-incremental"] = GetBuildProp(
847 "ro.build.version.incremental", OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800848
849 source_boot = common.GetBootableImage(
850 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
851 OPTIONS.source_info_dict)
852 target_boot = common.GetBootableImage(
853 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
854 updating_boot = (not OPTIONS.two_step and
855 (source_boot.data != target_boot.data))
856
Geremy Condra36bd3652014-02-06 19:45:10 -0800857 target_recovery = common.GetBootableImage(
858 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800859
Doug Zongkerfc44a512014-08-26 13:10:25 -0700860 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
861 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700862
863 blockimgdiff_version = 1
864 if OPTIONS.info_dict:
865 blockimgdiff_version = max(
866 int(i) for i in
867 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
868
Tianjie Xufc3422a2015-12-15 11:53:59 -0800869 # Check first block of system partition for remount R/W only if
870 # disk type is ext4
871 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
Tao Baob4cfca52016-02-04 14:26:02 -0800872 check_first_block = system_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700873 # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
874 # in zip formats. However with squashfs, a) all files are compressed in LZ4;
875 # b) the blocks listed in block map may not contain all the bytes for a given
876 # file (because they're rounded to be 4K-aligned).
877 disable_imgdiff = system_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700878 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800879 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700880 version=blockimgdiff_version,
881 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700882
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700883 if HasVendorPartition(target_zip):
884 if not HasVendorPartition(source_zip):
885 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700886 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
887 OPTIONS.source_info_dict)
888 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
889 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800890
891 # Check first block of vendor partition for remount R/W only if
892 # disk type is ext4
893 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baob4cfca52016-02-04 14:26:02 -0800894 check_first_block = vendor_partition.fs_type == "ext4"
Tao Bao293fd132016-06-11 12:19:23 -0700895 disable_imgdiff = vendor_partition.fs_type == "squashfs"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700896 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800897 check_first_block,
Tao Bao293fd132016-06-11 12:19:23 -0700898 version=blockimgdiff_version,
899 disable_imgdiff=disable_imgdiff)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700900 else:
901 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800902
Michael Rungec6e3afd2014-05-05 11:55:47 -0700903 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800904 device_specific.IncrementalOTA_Assertions()
905
906 # Two-step incremental package strategy (in chronological order,
907 # which is *not* the order in which the generated script has
908 # things):
909 #
910 # if stage is not "2/3" or "3/3":
911 # do verification on current system
912 # write recovery image to boot partition
913 # set stage to "2/3"
914 # reboot to boot partition and restart recovery
915 # else if stage is "2/3":
916 # write recovery image to recovery partition
917 # set stage to "3/3"
918 # reboot to recovery partition and restart recovery
919 # else:
920 # (stage must be "3/3")
921 # perform update:
922 # patch system files, etc.
923 # force full install of new boot image
924 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700925 # complete script normally
926 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800927
928 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700929 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800930 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700931 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800932 assert fs.fs_type.upper() == "EMMC", \
933 "two-step packages only supported on devices with EMMC /misc partitions"
934 bcb_dev = {"bcb_dev": fs.device}
935 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
936 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700937if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800938""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700939 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800940 script.WriteRawImage("/recovery", "recovery.img")
941 script.AppendExtra("""
942set_stage("%(bcb_dev)s", "3/3");
943reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700944else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800945""" % bcb_dev)
946
Tao Bao6c55a8a2015-04-08 15:30:27 -0700947 # Dump fingerprints
948 script.Print("Source: %s" % CalculateFingerprint(
949 oem_props, oem_dict, OPTIONS.source_info_dict))
950 script.Print("Target: %s" % CalculateFingerprint(
951 oem_props, oem_dict, OPTIONS.target_info_dict))
952
Geremy Condra36bd3652014-02-06 19:45:10 -0800953 script.Print("Verifying current system...")
954
955 device_specific.IncrementalOTA_VerifyBegin()
956
Michael Rungec6e3afd2014-05-05 11:55:47 -0700957 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700958 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
959 # patching on a device that's already on the target build will damage the
960 # system. Because operations like move don't check the block state, they
961 # always apply the changes unconditionally.
962 if blockimgdiff_version <= 2:
963 script.AssertSomeFingerprint(source_fp)
964 else:
965 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700966 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700967 if blockimgdiff_version <= 2:
968 script.AssertSomeThumbprint(
969 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
970 else:
971 script.AssertSomeThumbprint(
972 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
973 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800974
Tao Baob4cfca52016-02-04 14:26:02 -0800975 # Check the required cache size (i.e. stashed blocks).
976 size = []
977 if system_diff:
978 size.append(system_diff.required_cache)
979 if vendor_diff:
980 size.append(vendor_diff.required_cache)
981
Geremy Condra36bd3652014-02-06 19:45:10 -0800982 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700983 boot_type, boot_device = common.GetTypeAndDevice(
984 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800985 d = common.Difference(target_boot, source_boot)
986 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700987 if d is None:
988 include_full_boot = True
989 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
990 else:
991 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800992
Doug Zongkerf8340082014-08-05 10:39:37 -0700993 print "boot target: %d source: %d diff: %d" % (
994 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800995
Doug Zongkerf8340082014-08-05 10:39:37 -0700996 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800997
Doug Zongkerf8340082014-08-05 10:39:37 -0700998 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
999 (boot_type, boot_device,
1000 source_boot.size, source_boot.sha1,
1001 target_boot.size, target_boot.sha1))
Tao Baob4cfca52016-02-04 14:26:02 -08001002 size.append(target_boot.size)
1003
1004 if size:
1005 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -08001006
1007 device_specific.IncrementalOTA_VerifyEnd()
1008
1009 if OPTIONS.two_step:
1010 script.WriteRawImage("/boot", "recovery.img")
1011 script.AppendExtra("""
1012set_stage("%(bcb_dev)s", "2/3");
1013reboot_now("%(bcb_dev)s", "");
1014else
1015""" % bcb_dev)
1016
Jesse Zhao75bcea02015-01-06 10:59:53 -08001017 # Verify the existing partitions.
Tao Baod522bdc2016-04-12 15:53:16 -07001018 system_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001019 if vendor_diff:
Tao Baod522bdc2016-04-12 15:53:16 -07001020 vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001021
Geremy Condra36bd3652014-02-06 19:45:10 -08001022 script.Comment("---- start making changes here ----")
1023
1024 device_specific.IncrementalOTA_InstallBegin()
1025
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001026 system_diff.WriteScript(script, output_zip,
1027 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -07001028
Doug Zongkerfc44a512014-08-26 13:10:25 -07001029 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001030 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -08001031
1032 if OPTIONS.two_step:
1033 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1034 script.WriteRawImage("/boot", "boot.img")
1035 print "writing full boot image (forced by two-step mode)"
1036
1037 if not OPTIONS.two_step:
1038 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -07001039 if include_full_boot:
1040 print "boot image changed; including full."
1041 script.Print("Installing boot image...")
1042 script.WriteRawImage("/boot", "boot.img")
1043 else:
1044 # Produce the boot image by applying a patch to the current
1045 # contents of the boot partition, and write it back to the
1046 # partition.
1047 print "boot image changed; including patch."
1048 script.Print("Patching boot image...")
1049 script.ShowProgress(0.1, 10)
1050 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1051 % (boot_type, boot_device,
1052 source_boot.size, source_boot.sha1,
1053 target_boot.size, target_boot.sha1),
1054 "-",
1055 target_boot.size, target_boot.sha1,
1056 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001057 else:
1058 print "boot image unchanged; skipping."
1059
1060 # Do device-specific installation (eg, write radio image).
1061 device_specific.IncrementalOTA_InstallEnd()
1062
1063 if OPTIONS.extra_script is not None:
1064 script.AppendExtra(OPTIONS.extra_script)
1065
Doug Zongker922206e2014-03-04 13:16:24 -08001066 if OPTIONS.wipe_user_data:
1067 script.Print("Erasing user data...")
1068 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001069 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001070
Geremy Condra36bd3652014-02-06 19:45:10 -08001071 if OPTIONS.two_step:
1072 script.AppendExtra("""
1073set_stage("%(bcb_dev)s", "");
1074endif;
1075endif;
1076""" % bcb_dev)
1077
1078 script.SetProgress(1)
Tao Baofa41fb22016-03-08 17:53:39 -08001079 # For downgrade OTAs, we prefer to use the update-binary in the source
1080 # build that is actually newer than the one in the target build.
1081 if OPTIONS.downgrade:
1082 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1083 else:
1084 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001085 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001086 WriteMetadata(metadata, output_zip)
1087
Doug Zongker32b527d2014-03-04 10:03:02 -08001088
Tao Bao9bc6bb22015-11-09 16:58:28 -08001089def WriteVerifyPackage(input_zip, output_zip):
1090 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1091
1092 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1093 recovery_mount_options = OPTIONS.info_dict.get(
1094 "recovery_mount_options")
1095 oem_dict = None
1096 if oem_props is not None and len(oem_props) > 0:
1097 if OPTIONS.oem_source is None:
1098 raise common.ExternalError("OEM source required for this build")
1099 script.Mount("/oem", recovery_mount_options)
1100 oem_dict = common.LoadDictionaryFromLines(
1101 open(OPTIONS.oem_source).readlines())
1102
1103 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1104 metadata = {
1105 "post-build": target_fp,
1106 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1107 OPTIONS.info_dict),
1108 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1109 }
1110
1111 device_specific = common.DeviceSpecificParams(
1112 input_zip=input_zip,
1113 input_version=OPTIONS.info_dict["recovery_api_version"],
1114 output_zip=output_zip,
1115 script=script,
1116 input_tmp=OPTIONS.input_tmp,
1117 metadata=metadata,
1118 info_dict=OPTIONS.info_dict)
1119
1120 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1121
1122 script.Print("Verifying device images against %s..." % target_fp)
1123 script.AppendExtra("")
1124
1125 script.Print("Verifying boot...")
1126 boot_img = common.GetBootableImage(
1127 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1128 boot_type, boot_device = common.GetTypeAndDevice(
1129 "/boot", OPTIONS.info_dict)
1130 script.Verify("%s:%s:%d:%s" % (
1131 boot_type, boot_device, boot_img.size, boot_img.sha1))
1132 script.AppendExtra("")
1133
1134 script.Print("Verifying recovery...")
1135 recovery_img = common.GetBootableImage(
1136 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1137 recovery_type, recovery_device = common.GetTypeAndDevice(
1138 "/recovery", OPTIONS.info_dict)
1139 script.Verify("%s:%s:%d:%s" % (
1140 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1141 script.AppendExtra("")
1142
1143 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1144 system_tgt.ResetFileMap()
1145 system_diff = common.BlockDifference("system", system_tgt, src=None)
1146 system_diff.WriteStrictVerifyScript(script)
1147
1148 if HasVendorPartition(input_zip):
1149 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1150 vendor_tgt.ResetFileMap()
1151 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1152 vendor_diff.WriteStrictVerifyScript(script)
1153
1154 # Device specific partitions, such as radio, bootloader and etc.
1155 device_specific.VerifyOTA_Assertions()
1156
1157 script.SetProgress(1.0)
1158 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baob4cfca52016-02-04 14:26:02 -08001159 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001160 WriteMetadata(metadata, output_zip)
1161
1162
Tao Baoc098e9e2016-01-07 13:03:56 -08001163def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1164 source_file=None):
1165 """Generate an Android OTA package that has A/B update payload."""
1166
1167 # Setup signing keys.
1168 if OPTIONS.package_key is None:
1169 OPTIONS.package_key = OPTIONS.info_dict.get(
1170 "default_system_dev_certificate",
1171 "build/target/product/security/testkey")
1172
1173 # A/B updater expects key in RSA format.
1174 cmd = ["openssl", "pkcs8",
1175 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1176 "-inform", "DER", "-nocrypt"]
1177 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1178 cmd.extend(["-out", rsa_key])
1179 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1180 p1.wait()
1181 assert p1.returncode == 0, "openssl pkcs8 failed"
1182
1183 # Stage the output zip package for signing.
1184 temp_zip_file = tempfile.NamedTemporaryFile()
1185 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1186 compression=zipfile.ZIP_DEFLATED)
1187
1188 # Metadata to comply with Android OTA package format.
1189 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1190 oem_dict = None
1191 if oem_props:
1192 if OPTIONS.oem_source is None:
1193 raise common.ExternalError("OEM source required for this build")
1194 oem_dict = common.LoadDictionaryFromLines(
1195 open(OPTIONS.oem_source).readlines())
1196
1197 metadata = {
1198 "post-build": CalculateFingerprint(oem_props, oem_dict,
1199 OPTIONS.info_dict),
Tianjie Xud06f07e2016-06-09 14:18:45 -07001200 "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
1201 OPTIONS.info_dict),
Tao Baoc098e9e2016-01-07 13:03:56 -08001202 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1203 OPTIONS.info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001204 "ota-required-cache": "0",
1205 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001206 }
1207
1208 if source_file is not None:
1209 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1210 OPTIONS.source_info_dict)
Tianjie Xud06f07e2016-06-09 14:18:45 -07001211 metadata["pre-build-incremental"] = GetBuildProp(
1212 "ro.build.version.incremental", OPTIONS.source_info_dict)
Tao Baoc098e9e2016-01-07 13:03:56 -08001213
Tao Bao99c17252017-02-07 11:21:17 -08001214 HandleDowngradeMetadata(metadata)
1215 else:
1216 metadata["post-timestamp"] = GetBuildProp(
1217 "ro.build.date.utc", OPTIONS.info_dict)
1218
Tao Baoc098e9e2016-01-07 13:03:56 -08001219 # 1. Generate payload.
1220 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1221 cmd = ["brillo_update_payload", "generate",
1222 "--payload", payload_file,
1223 "--target_image", target_file]
1224 if source_file is not None:
1225 cmd.extend(["--source_image", source_file])
1226 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1227 p1.wait()
1228 assert p1.returncode == 0, "brillo_update_payload generate failed"
1229
1230 # 2. Generate hashes of the payload and metadata files.
1231 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1232 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1233 cmd = ["brillo_update_payload", "hash",
1234 "--unsigned_payload", payload_file,
1235 "--signature_size", "256",
1236 "--metadata_hash_file", metadata_sig_file,
1237 "--payload_hash_file", payload_sig_file]
1238 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1239 p1.wait()
1240 assert p1.returncode == 0, "brillo_update_payload hash failed"
1241
1242 # 3. Sign the hashes and insert them back into the payload file.
1243 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1244 suffix=".bin")
1245 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1246 suffix=".bin")
1247 # 3a. Sign the payload hash.
1248 cmd = ["openssl", "pkeyutl", "-sign",
1249 "-inkey", rsa_key,
1250 "-pkeyopt", "digest:sha256",
1251 "-in", payload_sig_file,
1252 "-out", signed_payload_sig_file]
1253 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1254 p1.wait()
1255 assert p1.returncode == 0, "openssl sign payload failed"
1256
1257 # 3b. Sign the metadata hash.
1258 cmd = ["openssl", "pkeyutl", "-sign",
1259 "-inkey", rsa_key,
1260 "-pkeyopt", "digest:sha256",
1261 "-in", metadata_sig_file,
1262 "-out", signed_metadata_sig_file]
1263 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1264 p1.wait()
1265 assert p1.returncode == 0, "openssl sign metadata failed"
1266
1267 # 3c. Insert the signatures back into the payload file.
1268 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1269 suffix=".bin")
1270 cmd = ["brillo_update_payload", "sign",
1271 "--unsigned_payload", payload_file,
1272 "--payload", signed_payload_file,
1273 "--signature_size", "256",
1274 "--metadata_signature_file", signed_metadata_sig_file,
1275 "--payload_signature_file", signed_payload_sig_file]
1276 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1277 p1.wait()
1278 assert p1.returncode == 0, "brillo_update_payload sign failed"
1279
Alex Deymo19241c12016-02-04 22:29:29 -08001280 # 4. Dump the signed payload properties.
1281 properties_file = common.MakeTempFile(prefix="payload-properties-",
1282 suffix=".txt")
1283 cmd = ["brillo_update_payload", "properties",
1284 "--payload", signed_payload_file,
1285 "--properties_file", properties_file]
1286 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1287 p1.wait()
1288 assert p1.returncode == 0, "brillo_update_payload properties failed"
1289
1290 # Add the signed payload file and properties into the zip.
1291 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001292 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1293 compress_type=zipfile.ZIP_STORED)
1294 WriteMetadata(metadata, output_zip)
1295
1296 # Sign the whole package to comply with the Android OTA package format.
1297 common.ZipClose(output_zip)
1298 SignOutput(temp_zip_file.name, output_file)
1299 temp_zip_file.close()
1300
1301
Dan Albert8b72aef2015-03-23 19:13:21 -07001302class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001303 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001304 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001305 print "Loading target..."
1306 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1307 print "Loading source..."
1308 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1309
1310 self.verbatim_targets = verbatim_targets = []
1311 self.patch_list = patch_list = []
1312 diffs = []
1313 self.renames = renames = {}
1314 known_paths = set()
1315 largest_source_size = 0
1316
1317 matching_file_cache = {}
1318 for fn, sf in source_data.items():
1319 assert fn == sf.name
1320 matching_file_cache["path:" + fn] = sf
1321 if fn in target_data.keys():
1322 AddToKnownPaths(fn, known_paths)
1323 # Only allow eligibility for filename/sha matching
1324 # if there isn't a perfect path match.
1325 if target_data.get(sf.name) is None:
1326 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1327 matching_file_cache["sha:" + sf.sha1] = sf
1328
1329 for fn in sorted(target_data.keys()):
1330 tf = target_data[fn]
1331 assert fn == tf.name
1332 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1333 if sf is not None and sf.name != tf.name:
1334 print "File has moved from " + sf.name + " to " + tf.name
1335 renames[sf.name] = tf
1336
1337 if sf is None or fn in OPTIONS.require_verbatim:
1338 # This file should be included verbatim
1339 if fn in OPTIONS.prohibit_verbatim:
1340 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1341 print "send", fn, "verbatim"
1342 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001343 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001344 if fn in target_data.keys():
1345 AddToKnownPaths(fn, known_paths)
1346 elif tf.sha1 != sf.sha1:
1347 # File is different; consider sending as a patch
1348 diffs.append(common.Difference(tf, sf))
1349 else:
1350 # Target file data identical to source (may still be renamed)
1351 pass
1352
1353 common.ComputeDifferences(diffs)
1354
1355 for diff in diffs:
1356 tf, sf, d = diff.GetPatch()
1357 path = "/".join(tf.name.split("/")[:-1])
1358 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1359 path not in known_paths:
1360 # patch is almost as big as the file; don't bother patching
1361 # or a patch + rename cannot take place due to the target
1362 # directory not existing
1363 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001364 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001365 if sf.name in renames:
1366 del renames[sf.name]
1367 AddToKnownPaths(tf.name, known_paths)
1368 else:
1369 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1370 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1371 largest_source_size = max(largest_source_size, sf.size)
1372
1373 self.largest_source_size = largest_source_size
1374
1375 def EmitVerification(self, script):
1376 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001377 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001378 if tf.name != sf.name:
1379 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1380 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1381 so_far += sf.size
1382 return so_far
1383
Michael Runge63f01de2014-10-28 19:24:19 -07001384 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001385 for fn, _, sha1 in self.verbatim_targets:
1386 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001387 script.FileCheck("/"+fn, sha1)
1388 for tf, _, _, _ in self.patch_list:
1389 script.FileCheck(tf.name, tf.sha1)
1390
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001391 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001392 file_list = ["/" + i[0] for i in self.verbatim_targets]
1393 file_list += ["/" + i for i in self.source_data
1394 if i not in self.target_data and i not in self.renames]
1395 file_list += list(extras)
1396 # Sort the list in descending order, which removes all the files first
1397 # before attempting to remove the folder. (Bug: 22960996)
1398 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001399
1400 def TotalPatchSize(self):
1401 return sum(i[1].size for i in self.patch_list)
1402
1403 def EmitPatches(self, script, total_patch_size, so_far):
1404 self.deferred_patch_list = deferred_patch_list = []
1405 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001406 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001407 if tf.name == "system/build.prop":
1408 deferred_patch_list.append(item)
1409 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001410 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001411 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001412 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1413 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001414 so_far += tf.size
1415 script.SetProgress(so_far / total_patch_size)
1416 return so_far
1417
1418 def EmitDeferredPatches(self, script):
1419 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001420 tf, sf, _, _ = item
1421 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1422 "patch/" + sf.name + ".p")
1423 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001424
1425 def EmitRenames(self, script):
1426 if len(self.renames) > 0:
1427 script.Print("Renaming files...")
1428 for src, tgt in self.renames.iteritems():
1429 print "Renaming " + src + " to " + tgt.name
1430 script.RenameFile(src, tgt.name)
1431
1432
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001433def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001434 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1435 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1436
Doug Zongker26e66192014-02-20 13:22:07 -08001437 if (OPTIONS.block_based and
1438 target_has_recovery_patch and
1439 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001440 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1441
Doug Zongker37974732010-09-16 17:44:38 -07001442 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1443 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001444
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001445 if source_version == 0:
1446 print ("WARNING: generating edify script for a source that "
1447 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001448 script = edify_generator.EdifyGenerator(
1449 source_version, OPTIONS.target_info_dict,
1450 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001451
Michael Runge6e836112014-04-15 17:40:21 -07001452 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001453 recovery_mount_options = OPTIONS.source_info_dict.get(
1454 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001455 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001456 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001457 if OPTIONS.oem_source is None:
1458 raise common.ExternalError("OEM source required for this build")
Tao Baobd25fcd2016-03-07 21:24:40 -08001459 if not OPTIONS.oem_no_mount:
1460 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001461 oem_dict = common.LoadDictionaryFromLines(
1462 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001463
Dan Albert8b72aef2015-03-23 19:13:21 -07001464 metadata = {
1465 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1466 OPTIONS.source_info_dict),
Tao Baob4cfca52016-02-04 14:26:02 -08001467 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001468 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001469
Tao Bao99c17252017-02-07 11:21:17 -08001470 HandleDowngradeMetadata(metadata)
Tao Bao4da324e2016-02-23 11:38:39 -08001471
Doug Zongker05d3dea2009-06-22 11:32:31 -07001472 device_specific = common.DeviceSpecificParams(
1473 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001474 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001475 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001476 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001477 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001478 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001479 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001480 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001481
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001482 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001483 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001484 if HasVendorPartition(target_zip):
1485 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001486 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001487 else:
1488 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001489
Dan Albert8b72aef2015-03-23 19:13:21 -07001490 target_fp = CalculateFingerprint(oem_props, oem_dict,
1491 OPTIONS.target_info_dict)
1492 source_fp = CalculateFingerprint(oem_props, oem_dict,
1493 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001494
1495 if oem_props is None:
1496 script.AssertSomeFingerprint(source_fp, target_fp)
1497 else:
1498 script.AssertSomeThumbprint(
1499 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1500 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1501
Doug Zongker2ea21062010-04-28 16:05:21 -07001502 metadata["pre-build"] = source_fp
1503 metadata["post-build"] = target_fp
Tianjie Xud06f07e2016-06-09 14:18:45 -07001504 metadata["pre-build-incremental"] = GetBuildProp(
1505 "ro.build.version.incremental", OPTIONS.source_info_dict)
1506 metadata["post-build-incremental"] = GetBuildProp(
1507 "ro.build.version.incremental", OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001508
Doug Zongker55d93282011-01-25 17:03:34 -08001509 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001510 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1511 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001512 target_boot = common.GetBootableImage(
1513 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001514 updating_boot = (not OPTIONS.two_step and
1515 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001516
Doug Zongker55d93282011-01-25 17:03:34 -08001517 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001518 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1519 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001520 target_recovery = common.GetBootableImage(
1521 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001522 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001523
Doug Zongker881dd402009-09-20 14:03:55 -07001524 # Here's how we divide up the progress bar:
1525 # 0.1 for verifying the start state (PatchCheck calls)
1526 # 0.8 for applying patches (ApplyPatch calls)
1527 # 0.1 for unpacking verbatim files, symlinking, and doing the
1528 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001529
Michael Runge6e836112014-04-15 17:40:21 -07001530 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001531 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001532
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001533 # Two-step incremental package strategy (in chronological order,
1534 # which is *not* the order in which the generated script has
1535 # things):
1536 #
1537 # if stage is not "2/3" or "3/3":
1538 # do verification on current system
1539 # write recovery image to boot partition
1540 # set stage to "2/3"
1541 # reboot to boot partition and restart recovery
1542 # else if stage is "2/3":
1543 # write recovery image to recovery partition
1544 # set stage to "3/3"
1545 # reboot to recovery partition and restart recovery
1546 # else:
1547 # (stage must be "3/3")
1548 # perform update:
1549 # patch system files, etc.
1550 # force full install of new boot image
1551 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001552 # complete script normally
1553 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001554
1555 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001556 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001557 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001558 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001559 assert fs.fs_type.upper() == "EMMC", \
1560 "two-step packages only supported on devices with EMMC /misc partitions"
1561 bcb_dev = {"bcb_dev": fs.device}
1562 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1563 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001564if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001565""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001566 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001567 script.WriteRawImage("/recovery", "recovery.img")
1568 script.AppendExtra("""
1569set_stage("%(bcb_dev)s", "3/3");
1570reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001571else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001572""" % bcb_dev)
1573
Tao Bao6c55a8a2015-04-08 15:30:27 -07001574 # Dump fingerprints
1575 script.Print("Source: %s" % (source_fp,))
1576 script.Print("Target: %s" % (target_fp,))
1577
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001578 script.Print("Verifying current system...")
1579
Doug Zongkere5ff5902012-01-17 10:55:37 -08001580 device_specific.IncrementalOTA_VerifyBegin()
1581
Doug Zongker881dd402009-09-20 14:03:55 -07001582 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001583 so_far = system_diff.EmitVerification(script)
1584 if vendor_diff:
1585 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001586
Tao Baob4cfca52016-02-04 14:26:02 -08001587 size = []
1588 if system_diff.patch_list:
1589 size.append(system_diff.largest_source_size)
1590 if vendor_diff:
1591 if vendor_diff.patch_list:
1592 size.append(vendor_diff.largest_source_size)
1593
Doug Zongker5da317e2009-06-02 13:38:17 -07001594 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001595 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001596 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001597 print "boot target: %d source: %d diff: %d" % (
1598 target_boot.size, source_boot.size, len(d))
1599
Doug Zongker048e7ca2009-06-15 14:31:53 -07001600 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001601
Tao Baodd24da92015-07-29 14:09:23 -07001602 boot_type, boot_device = common.GetTypeAndDevice(
1603 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001604
1605 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1606 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001607 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001608 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001609 so_far += source_boot.size
Tao Baob4cfca52016-02-04 14:26:02 -08001610 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001611
Tao Baob4cfca52016-02-04 14:26:02 -08001612 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001613 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001614
Doug Zongker05d3dea2009-06-22 11:32:31 -07001615 device_specific.IncrementalOTA_VerifyEnd()
1616
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001617 if OPTIONS.two_step:
1618 script.WriteRawImage("/boot", "recovery.img")
1619 script.AppendExtra("""
1620set_stage("%(bcb_dev)s", "2/3");
1621reboot_now("%(bcb_dev)s", "");
1622else
1623""" % bcb_dev)
1624
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001625 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001626
Doug Zongkere5ff5902012-01-17 10:55:37 -08001627 device_specific.IncrementalOTA_InstallBegin()
1628
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001629 if OPTIONS.two_step:
1630 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1631 script.WriteRawImage("/boot", "boot.img")
1632 print "writing full boot image (forced by two-step mode)"
1633
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001634 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001635 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1636 if vendor_diff:
1637 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001638
Doug Zongker881dd402009-09-20 14:03:55 -07001639 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001640 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1641 if vendor_diff:
1642 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001643 if updating_boot:
1644 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001645
1646 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001647 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1648 if vendor_diff:
1649 script.Print("Patching vendor files...")
1650 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001651
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001652 if not OPTIONS.two_step:
1653 if updating_boot:
1654 # Produce the boot image by applying a patch to the current
1655 # contents of the boot partition, and write it back to the
1656 # partition.
1657 script.Print("Patching boot image...")
1658 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1659 % (boot_type, boot_device,
1660 source_boot.size, source_boot.sha1,
1661 target_boot.size, target_boot.sha1),
1662 "-",
1663 target_boot.size, target_boot.sha1,
1664 source_boot.sha1, "patch/boot.img.p")
1665 so_far += target_boot.size
1666 script.SetProgress(so_far / total_patch_size)
1667 print "boot image changed; including."
1668 else:
1669 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001670
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001671 system_items = ItemSet("system", "META/filesystem_config.txt")
1672 if vendor_diff:
1673 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1674
Doug Zongkereef39442009-04-02 12:14:19 -07001675 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001676 # Recovery is generated as a patch using both the boot image
1677 # (which contains the same linux kernel as recovery) and the file
1678 # /system/etc/recovery-resource.dat (which contains all the images
1679 # used in the recovery UI) as sources. This lets us minimize the
1680 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001681 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001682 # For older builds where recovery-resource.dat is not present, we
1683 # use only the boot image as the source.
1684
Doug Zongkerc9253822014-02-04 12:17:58 -08001685 if not target_has_recovery_patch:
1686 def output_sink(fn, data):
1687 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001688 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001689
1690 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1691 target_recovery, target_boot)
1692 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001693 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001694 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001695 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001696 else:
1697 print "recovery image unchanged; skipping."
1698
Doug Zongker881dd402009-09-20 14:03:55 -07001699 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001700
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001701 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1702 if vendor_diff:
1703 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1704
1705 temp_script = script.MakeTemporary()
1706 system_items.GetMetadata(target_zip)
1707 system_items.Get("system").SetPermissions(temp_script)
1708 if vendor_diff:
1709 vendor_items.GetMetadata(target_zip)
1710 vendor_items.Get("vendor").SetPermissions(temp_script)
1711
1712 # Note that this call will mess up the trees of Items, so make sure
1713 # we're done with them.
1714 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1715 if vendor_diff:
1716 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001717
1718 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001719 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1720
1721 # Delete all the symlinks in source that aren't in target. This
1722 # needs to happen before verbatim files are unpacked, in case a
1723 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001724
1725 # If a symlink in the source will be replaced by a regular file, we cannot
1726 # delete the symlink/file in case the package gets applied again. For such
1727 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1728 # (Bug: 23646151)
1729 replaced_symlinks = dict()
1730 if system_diff:
1731 for i in system_diff.verbatim_targets:
1732 replaced_symlinks["/%s" % (i[0],)] = i[2]
1733 if vendor_diff:
1734 for i in vendor_diff.verbatim_targets:
1735 replaced_symlinks["/%s" % (i[0],)] = i[2]
1736
1737 if system_diff:
1738 for tf in system_diff.renames.values():
1739 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1740 if vendor_diff:
1741 for tf in vendor_diff.renames.values():
1742 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1743
1744 always_delete = []
1745 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001746 for dest, link in source_symlinks:
1747 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001748 if link in replaced_symlinks:
1749 may_delete.append((link, replaced_symlinks[link]))
1750 else:
1751 always_delete.append(link)
1752 script.DeleteFiles(always_delete)
1753 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001754
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001755 if system_diff.verbatim_targets:
1756 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001757 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001758 if vendor_diff and vendor_diff.verbatim_targets:
1759 script.Print("Unpacking new vendor files...")
1760 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001761
Doug Zongkerc9253822014-02-04 12:17:58 -08001762 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001763 script.Print("Unpacking new recovery...")
1764 script.UnpackPackageDir("recovery", "/system")
1765
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001766 system_diff.EmitRenames(script)
1767 if vendor_diff:
1768 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001769
Doug Zongker05d3dea2009-06-22 11:32:31 -07001770 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001771
1772 # Create all the symlinks that don't already exist, or point to
1773 # somewhere different than what we want. Delete each symlink before
1774 # creating it, since the 'symlink' command won't overwrite.
1775 to_create = []
1776 for dest, link in target_symlinks:
1777 if link in source_symlinks_d:
1778 if dest != source_symlinks_d[link]:
1779 to_create.append((dest, link))
1780 else:
1781 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001782 script.DeleteFiles([i[1] for i in to_create])
1783 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001784
1785 # Now that the symlinks are created, we can set all the
1786 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001787 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001788
Doug Zongker881dd402009-09-20 14:03:55 -07001789 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001790 device_specific.IncrementalOTA_InstallEnd()
1791
Doug Zongker1c390a22009-05-14 19:06:36 -07001792 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001793 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001794
Doug Zongkere92f15a2011-08-26 13:46:40 -07001795 # Patch the build.prop file last, so if something fails but the
1796 # device can still come up, it appears to be the old build and will
1797 # get set the OTA package again to retry.
1798 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001799 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001800
Doug Zongker922206e2014-03-04 13:16:24 -08001801 if OPTIONS.wipe_user_data:
1802 script.Print("Erasing user data...")
1803 script.FormatPartition("/data")
Tao Bao4da324e2016-02-23 11:38:39 -08001804 metadata["ota-wipe"] = "yes"
Doug Zongker922206e2014-03-04 13:16:24 -08001805
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001806 if OPTIONS.two_step:
1807 script.AppendExtra("""
1808set_stage("%(bcb_dev)s", "");
1809endif;
1810endif;
1811""" % bcb_dev)
1812
Michael Runge63f01de2014-10-28 19:24:19 -07001813 if OPTIONS.verify and system_diff:
1814 script.Print("Remounting and verifying system partition files...")
1815 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001816 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001817 system_diff.EmitExplicitTargetVerification(script)
1818
1819 if OPTIONS.verify and vendor_diff:
1820 script.Print("Remounting and verifying vendor partition files...")
1821 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001822 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001823 vendor_diff.EmitExplicitTargetVerification(script)
Tao Baofa41fb22016-03-08 17:53:39 -08001824
1825 # For downgrade OTAs, we prefer to use the update-binary in the source
1826 # build that is actually newer than the one in the target build.
1827 if OPTIONS.downgrade:
1828 script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
1829 else:
1830 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001831
Tao Baob4cfca52016-02-04 14:26:02 -08001832 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001833 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001834
1835
1836def main(argv):
1837
1838 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001839 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001840 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001841 elif o in ("-k", "--package_key"):
1842 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001843 elif o in ("-i", "--incremental_from"):
1844 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001845 elif o == "--full_radio":
1846 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001847 elif o == "--full_bootloader":
1848 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001849 elif o in ("-w", "--wipe_user_data"):
1850 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001851 elif o in ("-n", "--no_prereq"):
1852 OPTIONS.omit_prereq = True
Tao Bao4da324e2016-02-23 11:38:39 -08001853 elif o == "--downgrade":
1854 OPTIONS.downgrade = True
1855 OPTIONS.wipe_user_data = True
Michael Runge6e836112014-04-15 17:40:21 -07001856 elif o in ("-o", "--oem_settings"):
1857 OPTIONS.oem_source = a
Tao Baodf4cb0b2016-02-25 19:49:55 -08001858 elif o == "--oem_no_mount":
1859 OPTIONS.oem_no_mount = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001860 elif o in ("-e", "--extra_script"):
1861 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001862 elif o in ("-a", "--aslr_mode"):
1863 if a in ("on", "On", "true", "True", "yes", "Yes"):
1864 OPTIONS.aslr_mode = True
1865 else:
1866 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001867 elif o in ("-t", "--worker_threads"):
1868 if a.isdigit():
1869 OPTIONS.worker_threads = int(a)
1870 else:
1871 raise ValueError("Cannot parse value %r for option %r - only "
1872 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001873 elif o in ("-2", "--two_step"):
1874 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001875 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001876 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001877 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001878 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001879 elif o == "--block":
1880 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001881 elif o in ("-b", "--binary"):
1882 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001883 elif o in ("--no_fallback_to_full",):
1884 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001885 elif o == "--stash_threshold":
1886 try:
1887 OPTIONS.stash_threshold = float(a)
1888 except ValueError:
1889 raise ValueError("Cannot parse value %r for option %r - expecting "
1890 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001891 elif o == "--gen_verify":
1892 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001893 elif o == "--log_diff":
1894 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001895 else:
1896 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001897 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001898
1899 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001900 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001901 extra_long_opts=[
1902 "board_config=",
1903 "package_key=",
1904 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001905 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001906 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001907 "wipe_user_data",
1908 "no_prereq",
Tao Bao4da324e2016-02-23 11:38:39 -08001909 "downgrade",
Dan Albert8b72aef2015-03-23 19:13:21 -07001910 "extra_script=",
1911 "worker_threads=",
1912 "aslr_mode=",
1913 "two_step",
1914 "no_signing",
1915 "block",
1916 "binary=",
1917 "oem_settings=",
Tao Baodf4cb0b2016-02-25 19:49:55 -08001918 "oem_no_mount",
Dan Albert8b72aef2015-03-23 19:13:21 -07001919 "verify",
1920 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001921 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001922 "gen_verify",
1923 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001924 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001925
1926 if len(args) != 2:
1927 common.Usage(__doc__)
1928 sys.exit(1)
1929
Tao Bao4da324e2016-02-23 11:38:39 -08001930 if OPTIONS.downgrade:
1931 # Sanity check to enforce a data wipe.
1932 if not OPTIONS.wipe_user_data:
1933 raise ValueError("Cannot downgrade without a data wipe")
1934
1935 # We should only allow downgrading incrementals (as opposed to full).
1936 # Otherwise the device may go back from arbitrary build with this full
1937 # OTA package.
1938 if OPTIONS.incremental_source is None:
1939 raise ValueError("Cannot generate downgradable full OTAs - consider"
1940 "using --omit_prereq?")
1941
Tao Baoc098e9e2016-01-07 13:03:56 -08001942 # Load the dict file from the zip directly to have a peek at the OTA type.
1943 # For packages using A/B update, unzipping is not needed.
1944 input_zip = zipfile.ZipFile(args[0], "r")
1945 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1946 common.ZipClose(input_zip)
1947
1948 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1949
1950 if ab_update:
1951 if OPTIONS.incremental_source is not None:
1952 OPTIONS.target_info_dict = OPTIONS.info_dict
1953 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1954 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1955 common.ZipClose(source_zip)
1956
1957 if OPTIONS.verbose:
1958 print "--- target info ---"
1959 common.DumpInfoDict(OPTIONS.info_dict)
1960
1961 if OPTIONS.incremental_source is not None:
1962 print "--- source info ---"
1963 common.DumpInfoDict(OPTIONS.source_info_dict)
1964
1965 WriteABOTAPackageWithBrilloScript(
1966 target_file=args[0],
1967 output_file=args[1],
1968 source_file=OPTIONS.incremental_source)
1969
1970 print "done."
1971 return
1972
Doug Zongker1c390a22009-05-14 19:06:36 -07001973 if OPTIONS.extra_script is not None:
1974 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1975
Doug Zongkereef39442009-04-02 12:14:19 -07001976 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001977 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001978
Doug Zongkereef39442009-04-02 12:14:19 -07001979 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001980 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001981
Doug Zongker37974732010-09-16 17:44:38 -07001982 if OPTIONS.verbose:
1983 print "--- target info ---"
1984 common.DumpInfoDict(OPTIONS.info_dict)
1985
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001986 # If the caller explicitly specified the device-specific extensions
1987 # path via -s/--device_specific, use that. Otherwise, use
1988 # META/releasetools.py if it is present in the target target_files.
1989 # Otherwise, take the path of the file from 'tool_extensions' in the
1990 # info dict and look for that in the local filesystem, relative to
1991 # the current directory.
1992
Doug Zongker37974732010-09-16 17:44:38 -07001993 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001994 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1995 if os.path.exists(from_input):
1996 print "(using device-specific extensions from target_files)"
1997 OPTIONS.device_specific = from_input
1998 else:
1999 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
2000
Doug Zongker37974732010-09-16 17:44:38 -07002001 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08002002 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07002003
Tao Baoc098e9e2016-01-07 13:03:56 -08002004 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07002005 raise common.ExternalError(
2006 "--- target build has specified no recovery ---")
2007
Tao Bao767e3ac2015-11-10 12:19:19 -08002008 # Use the default key to sign the package if not specified with package_key.
2009 if not OPTIONS.no_signing:
2010 if OPTIONS.package_key is None:
2011 OPTIONS.package_key = OPTIONS.info_dict.get(
2012 "default_system_dev_certificate",
2013 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07002014
Tao Bao767e3ac2015-11-10 12:19:19 -08002015 # Set up the output zip. Create a temporary zip file if signing is needed.
2016 if OPTIONS.no_signing:
2017 if os.path.exists(args[1]):
2018 os.unlink(args[1])
2019 output_zip = zipfile.ZipFile(args[1], "w",
2020 compression=zipfile.ZIP_DEFLATED)
2021 else:
2022 temp_zip_file = tempfile.NamedTemporaryFile()
2023 output_zip = zipfile.ZipFile(temp_zip_file, "w",
2024 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07002025
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08002026 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08002027 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08002028 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08002029 print "--- can't determine the cache partition size ---"
2030 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07002031
Tao Bao9bc6bb22015-11-09 16:58:28 -08002032 # Generate a verify package.
2033 if OPTIONS.gen_verify:
2034 WriteVerifyPackage(input_zip, output_zip)
2035
Tao Bao767e3ac2015-11-10 12:19:19 -08002036 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08002037 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08002038 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08002039
2040 # Generate an incremental OTA. It will fall back to generate a full OTA on
2041 # failure unless no_fallback_to_full is specified.
2042 else:
2043 print "unzipping source target-files..."
2044 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
2045 OPTIONS.incremental_source)
2046 OPTIONS.target_info_dict = OPTIONS.info_dict
2047 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
2048 OPTIONS.source_tmp)
2049 if OPTIONS.verbose:
2050 print "--- source info ---"
2051 common.DumpInfoDict(OPTIONS.source_info_dict)
2052 try:
2053 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08002054 if OPTIONS.log_diff:
2055 out_file = open(OPTIONS.log_diff, 'w')
2056 import target_files_diff
2057 target_files_diff.recursiveDiff('',
2058 OPTIONS.source_tmp,
2059 OPTIONS.input_tmp,
2060 out_file)
2061 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08002062 except ValueError:
2063 if not OPTIONS.fallback_to_full:
2064 raise
2065 print "--- failed to build incremental; falling back to full ---"
2066 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07002067 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07002068
Tao Bao767e3ac2015-11-10 12:19:19 -08002069 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07002070
Tao Bao767e3ac2015-11-10 12:19:19 -08002071 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09002072 if not OPTIONS.no_signing:
2073 SignOutput(temp_zip_file.name, args[1])
2074 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07002075
Doug Zongkereef39442009-04-02 12:14:19 -07002076 print "done."
2077
2078
2079if __name__ == '__main__':
2080 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08002081 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07002082 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07002083 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07002084 print
2085 print " ERROR: %s" % (e,)
2086 print
2087 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07002088 finally:
2089 common.Cleanup()