blob: d0636b672fca33955df20a912104e4d3c9e9789a [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
Doug Zongkerdbfaae52009-04-21 17:12:54 -070057 -w (--wipe_user_data)
58 Generate an OTA package that will wipe the user data partition
59 when installed.
60
Doug Zongker962069c2009-04-23 11:41:58 -070061 -n (--no_prereq)
62 Omit the timestamp prereq check normally included at the top of
63 the build scripts (used for developer OTA packages which
64 legitimately need to go back and forth).
65
Doug Zongker1c390a22009-05-14 19:06:36 -070066 -e (--extra_script) <file>
67 Insert the contents of file at the end of the update script.
68
Hristo Bojinovdafb0422010-08-26 14:35:16 -070069 -a (--aslr_mode) <on|off>
70 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050071
Doug Zongker9b23f2c2013-11-25 14:44:12 -080072 -2 (--two_step)
73 Generate a 'two-step' OTA package, where recovery is updated
74 first, so that any changes made to the system partition are done
75 using the new recovery (new kernel, etc.).
76
Doug Zongker26e66192014-02-20 13:22:07 -080077 --block
78 Generate a block-based OTA if possible. Will fall back to a
79 file-based OTA if the target_files is older and doesn't support
80 block-based OTAs.
81
Doug Zongker25568482014-03-03 10:21:27 -080082 -b (--binary) <file>
83 Use the given binary as the update-binary in the output package,
84 instead of the binary in the build's target_files. Use for
85 development only.
86
Martin Blumenstingl374e1142014-05-31 20:42:55 +020087 -t (--worker_threads) <int>
88 Specifies the number of worker-threads that will be used when
89 generating patches for incremental updates (defaults to 3).
90
Tao Bao8dcf7382015-05-21 14:09:49 -070091 --stash_threshold <float>
92 Specifies the threshold that will be used to compute the maximum
93 allowed stash size (defaults to 0.8).
Tao Bao9bc6bb22015-11-09 16:58:28 -080094
95 --gen_verify
96 Generate an OTA package that verifies the partitions.
Tao Baod62c6032015-11-30 09:40:20 -080097
98 --log_diff <file>
99 Generate a log file that shows the differences in the source and target
100 builds for an incremental package. This option is only meaningful when
101 -i is specified.
Doug Zongkereef39442009-04-02 12:14:19 -0700102"""
103
104import sys
105
Doug Zongkercf6d5a92014-02-18 10:57:07 -0800106if sys.hexversion < 0x02070000:
107 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -0700108 sys.exit(1)
109
Doug Zongkerfc44a512014-08-26 13:10:25 -0700110import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -0700111import os
Doug Zongkereef39442009-04-02 12:14:19 -0700112import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700113import zipfile
114
115import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700116import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700117import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700118
119OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700120OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700121OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700122OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700123OPTIONS.require_verbatim = set()
124OPTIONS.prohibit_verbatim = set(("system/build.prop",))
125OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700126OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700127OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700128OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700129OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700130OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
131if OPTIONS.worker_threads == 0:
132 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800133OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900134OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800135OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800136OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700137OPTIONS.oem_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -0700138OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700139OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700140OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700141# Stash size cannot exceed cache_size * threshold.
142OPTIONS.cache_size = None
143OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800144OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800145OPTIONS.log_diff = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700146
Doug Zongkereef39442009-04-02 12:14:19 -0700147def MostPopularKey(d, default):
148 """Given a dict, return the key corresponding to the largest
149 value. Returns 'default' if the dict is empty."""
150 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700151 if not x:
152 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700153 x.sort()
154 return x[-1][1]
155
156
157def IsSymlink(info):
158 """Return true if the zipfile.ZipInfo object passed in represents a
159 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700160 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700161
Hristo Bojinov96be7202010-08-02 10:26:17 -0700162def IsRegular(info):
163 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700164 regular file."""
165 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700166
Michael Runge4038aa82013-12-13 18:06:28 -0800167def ClosestFileMatch(src, tgtfiles, existing):
168 """Returns the closest file match between a source file and list
169 of potential matches. The exact filename match is preferred,
170 then the sha1 is searched for, and finally a file with the same
171 basename is evaluated. Rename support in the updater-binary is
172 required for the latter checks to be used."""
173
174 result = tgtfiles.get("path:" + src.name)
175 if result is not None:
176 return result
177
178 if not OPTIONS.target_info_dict.get("update_rename_support", False):
179 return None
180
181 if src.size < 1000:
182 return None
183
184 result = tgtfiles.get("sha1:" + src.sha1)
185 if result is not None and existing.get(result.name) is None:
186 return result
187 result = tgtfiles.get("file:" + src.name.split("/")[-1])
188 if result is not None and existing.get(result.name) is None:
189 return result
190 return None
191
Dan Albert8b72aef2015-03-23 19:13:21 -0700192class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700193 def __init__(self, partition, fs_config):
194 self.partition = partition
195 self.fs_config = fs_config
196 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700197
Dan Albert8b72aef2015-03-23 19:13:21 -0700198 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700199 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700200 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700201 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700202
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700203 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700204 # The target_files contains a record of what the uid,
205 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700206 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700207
208 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700209 if not line:
210 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700211 columns = line.split()
212 name, uid, gid, mode = columns[:4]
213 selabel = None
214 capabilities = None
215
216 # After the first 4 columns, there are a series of key=value
217 # pairs. Extract out the fields we care about.
218 for element in columns[4:]:
219 key, value = element.split("=")
220 if key == "selabel":
221 selabel = value
222 if key == "capabilities":
223 capabilities = value
224
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700225 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700226 if i is not None:
227 i.uid = int(uid)
228 i.gid = int(gid)
229 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700230 i.selabel = selabel
231 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700232 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700233 i.children.sort(key=lambda i: i.name)
234
Tao Baof2cffbd2015-07-22 12:33:18 -0700235 # Set metadata for the files generated by this script. For full recovery
236 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700237 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700238 if i:
239 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700240 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700241 if i:
242 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700243
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700244
Dan Albert8b72aef2015-03-23 19:13:21 -0700245class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700246 """Items represent the metadata (user, group, mode) of files and
247 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700248 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700249 self.itemset = itemset
250 self.name = name
251 self.uid = None
252 self.gid = None
253 self.mode = None
254 self.selabel = None
255 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700256 self.is_dir = is_dir
257 self.descendants = None
258 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700259
260 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700261 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700262 self.parent.children.append(self)
263 else:
264 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700265 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700266 self.children = []
267
268 def Dump(self, indent=0):
269 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700270 print "%s%s %d %d %o" % (
271 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700272 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700273 print "%s%s %s %s %s" % (
274 " " * indent, self.name, self.uid, self.gid, self.mode)
275 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700276 print "%s%s" % (" "*indent, self.descendants)
277 print "%s%s" % (" "*indent, self.best_subtree)
278 for i in self.children:
279 i.Dump(indent=indent+1)
280
Doug Zongkereef39442009-04-02 12:14:19 -0700281 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700282 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700283 all children and determine the best strategy for using set_perm_recursive
284 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700285 values. Recursively calls itself for all descendants.
286
Dan Albert8b72aef2015-03-23 19:13:21 -0700287 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
288 counting up all descendants of this node. (dmode or fmode may be None.)
289 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
290 fmode, selabel, capabilities) tuple that will match the most descendants of
291 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700292 """
293
Dan Albert8b72aef2015-03-23 19:13:21 -0700294 assert self.is_dir
295 key = (self.uid, self.gid, self.mode, None, self.selabel,
296 self.capabilities)
297 self.descendants = {key: 1}
298 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700299 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700300 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700301 for k, v in i.CountChildMetadata().iteritems():
302 d[k] = d.get(k, 0) + v
303 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700304 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700305 d[k] = d.get(k, 0) + 1
306
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700307 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
308 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700309
310 # First, find the (uid, gid) pair that matches the most
311 # descendants.
312 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700313 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700314 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
315 ug = MostPopularKey(ug, (0, 0))
316
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700317 # Now find the dmode, fmode, selabel, and capabilities that match
318 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700319 best_dmode = (0, 0o755)
320 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700321 best_selabel = (0, None)
322 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700323 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700324 if k[:2] != ug:
325 continue
326 if k[2] is not None and count >= best_dmode[0]:
327 best_dmode = (count, k[2])
328 if k[3] is not None and count >= best_fmode[0]:
329 best_fmode = (count, k[3])
330 if k[4] is not None and count >= best_selabel[0]:
331 best_selabel = (count, k[4])
332 if k[5] is not None and count >= best_capabilities[0]:
333 best_capabilities = (count, k[5])
334 self.best_subtree = ug + (
335 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700336
337 return d
338
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700339 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700340 """Append set_perm/set_perm_recursive commands to 'script' to
341 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700342 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700343
344 self.CountChildMetadata()
345
346 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700347 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
348 # that the current item (and all its children) have already been set to.
349 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700350 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700351 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700352 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700353 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700354 current = item.best_subtree
355
356 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700357 item.mode != current[2] or item.selabel != current[4] or \
358 item.capabilities != current[5]:
359 script.SetPermissions("/"+item.name, item.uid, item.gid,
360 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700361
362 for i in item.children:
363 recurse(i, current)
364 else:
365 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700366 item.mode != current[3] or item.selabel != current[4] or \
367 item.capabilities != current[5]:
368 script.SetPermissions("/"+item.name, item.uid, item.gid,
369 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700370
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700371 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700372
373
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700374def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
375 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700376 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800377 list of symlinks. output_zip may be None, in which case the copy is
378 skipped (but the other side effects still happen). substitute is an
379 optional dict of {output filename: contents} to be output instead of
380 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700381 """
382
383 symlinks = []
384
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700385 partition = itemset.partition
386
Doug Zongkereef39442009-04-02 12:14:19 -0700387 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700388 prefix = partition.upper() + "/"
389 if info.filename.startswith(prefix):
390 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700391 if IsSymlink(info):
392 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700393 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700394 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700395 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700396 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700397 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700398 if substitute and fn in substitute and substitute[fn] is None:
399 continue
400 if output_zip is not None:
401 if substitute and fn in substitute:
402 data = substitute[fn]
403 else:
404 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700405 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700406 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700407 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700408 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700409 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700410
411 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800412 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700413
414
Doug Zongkereef39442009-04-02 12:14:19 -0700415def SignOutput(temp_zip_name, output_zip_name):
416 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
417 pw = key_passwords[OPTIONS.package_key]
418
Doug Zongker951495f2009-08-14 12:44:19 -0700419 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
420 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700421
422
Dan Albert8b72aef2015-03-23 19:13:21 -0700423def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700424 oem_props = info_dict.get("oem_fingerprint_properties")
Michael Runge560569a2014-09-18 15:12:45 -0700425 if oem_props is None or len(oem_props) == 0:
Michael Runge6e836112014-04-15 17:40:21 -0700426 device = GetBuildProp("ro.product.device", info_dict)
427 script.AssertDevice(device)
428 else:
429 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700430 raise common.ExternalError(
431 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700432 for prop in oem_props.split():
433 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700434 raise common.ExternalError(
435 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700436 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700437
Doug Zongkereef39442009-04-02 12:14:19 -0700438
Doug Zongkerc9253822014-02-04 12:17:58 -0800439def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700440 namelist = [name for name in target_files_zip.namelist()]
441 return ("SYSTEM/recovery-from-boot.p" in namelist or
442 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700443
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700444def HasVendorPartition(target_files_zip):
445 try:
446 target_files_zip.getinfo("VENDOR/")
447 return True
448 except KeyError:
449 return False
450
Michael Runge6e836112014-04-15 17:40:21 -0700451def GetOemProperty(name, oem_props, oem_dict, info_dict):
452 if oem_props is not None and name in oem_props:
453 return oem_dict[name]
454 return GetBuildProp(name, info_dict)
455
456
457def CalculateFingerprint(oem_props, oem_dict, info_dict):
458 if oem_props is None:
459 return GetBuildProp("ro.build.fingerprint", info_dict)
460 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700461 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
462 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
463 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
464 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700465
Doug Zongkerfc44a512014-08-26 13:10:25 -0700466
Doug Zongker3c84f562014-07-31 11:06:30 -0700467def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700468 # Return an image object (suitable for passing to BlockImageDiff)
469 # for the 'which' partition (most be "system" or "vendor"). If a
470 # prebuilt image and file map are found in tmpdir they are used,
471 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700472
473 assert which in ("system", "vendor")
474
475 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700476 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
477 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700478 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700479 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700480
481 else:
482 print "building %s.img from target-files" % (which,)
483
484 # This is an 'old' target-files, which does not contain images
485 # already built. Build them.
486
Doug Zongkerfc44a512014-08-26 13:10:25 -0700487 mappath = tempfile.mkstemp()[1]
488 OPTIONS.tempfiles.append(mappath)
489
Doug Zongker3c84f562014-07-31 11:06:30 -0700490 import add_img_to_target_files
491 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700492 path = add_img_to_target_files.BuildSystem(
493 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700494 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700495 path = add_img_to_target_files.BuildVendor(
496 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700497
Tao Baoff777812015-05-12 11:42:31 -0700498 # Bug: http://b/20939131
499 # In ext4 filesystems, block 0 might be changed even being mounted
500 # R/O. We add it to clobbered_blocks so that it will be written to the
501 # target unconditionally. Note that they are still part of care_map.
502 clobbered_blocks = "0"
503
504 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700505
506
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700507def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700508 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700509 # be installed on top of. For now, we expect the API just won't
510 # change very often. Similarly for fstab, it might have changed
511 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700512 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700513
Michael Runge6e836112014-04-15 17:40:21 -0700514 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700515 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700516 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700517 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700518 if OPTIONS.oem_source is None:
519 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700520 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700521 oem_dict = common.LoadDictionaryFromLines(
522 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700523
Dan Albert8b72aef2015-03-23 19:13:21 -0700524 metadata = {
525 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700526 OPTIONS.info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700527 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
528 OPTIONS.info_dict),
529 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
530 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700531
Doug Zongker05d3dea2009-06-22 11:32:31 -0700532 device_specific = common.DeviceSpecificParams(
533 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700534 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700535 output_zip=output_zip,
536 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700537 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700538 metadata=metadata,
539 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700540
Doug Zongkerc9253822014-02-04 12:17:58 -0800541 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800542 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800543
Doug Zongker962069c2009-04-23 11:41:58 -0700544 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700545 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700546 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
547 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700548
Michael Runge6e836112014-04-15 17:40:21 -0700549 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700550 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800551
552 # Two-step package strategy (in chronological order, which is *not*
553 # the order in which the generated script has things):
554 #
555 # if stage is not "2/3" or "3/3":
556 # write recovery image to boot partition
557 # set stage to "2/3"
558 # reboot to boot partition and restart recovery
559 # else if stage is "2/3":
560 # write recovery image to recovery partition
561 # set stage to "3/3"
562 # reboot to recovery partition and restart recovery
563 # else:
564 # (stage must be "3/3")
565 # set stage to ""
566 # do normal full package installation:
567 # wipe and install system, boot image, etc.
568 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700569 # complete script normally
570 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800571
572 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
573 OPTIONS.input_tmp, "RECOVERY")
574 if OPTIONS.two_step:
575 if not OPTIONS.info_dict.get("multistage_support", None):
576 assert False, "two-step packages not supported by this build"
577 fs = OPTIONS.info_dict["fstab"]["/misc"]
578 assert fs.fs_type.upper() == "EMMC", \
579 "two-step packages only supported on devices with EMMC /misc partitions"
580 bcb_dev = {"bcb_dev": fs.device}
581 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
582 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700583if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800584""" % bcb_dev)
585 script.WriteRawImage("/recovery", "recovery.img")
586 script.AppendExtra("""
587set_stage("%(bcb_dev)s", "3/3");
588reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700589else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800590""" % bcb_dev)
591
Tao Bao6c55a8a2015-04-08 15:30:27 -0700592 # Dump fingerprints
593 script.Print("Target: %s" % CalculateFingerprint(
594 oem_props, oem_dict, OPTIONS.info_dict))
595
Doug Zongkere5ff5902012-01-17 10:55:37 -0800596 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700597
Doug Zongker01ce19c2014-02-04 13:48:15 -0800598 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700599
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700600 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800601 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700602 if HasVendorPartition(input_zip):
603 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700604
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400605 # Place a copy of file_contexts.bin into the OTA package which will be used
606 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700607 if "selinux_fc" in OPTIONS.info_dict:
608 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500609
Michael Runge7cd99ba2014-10-22 17:21:48 -0700610 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
611
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700612 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700613 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800614
Doug Zongker26e66192014-02-20 13:22:07 -0800615 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700616 # Full OTA is done as an "incremental" against an empty source
617 # image. This has the effect of writing new data from the package
618 # to the entire partition, but lets us reuse the updater code that
619 # writes incrementals to do it.
620 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
621 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700622 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700623 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800624 else:
625 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700626 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800627 if not has_recovery_patch:
628 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800629 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700630
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700631 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800632 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700633
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700634 boot_img = common.GetBootableImage(
635 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800636
Doug Zongker91a99c22014-05-09 13:15:01 -0700637 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800638 def output_sink(fn, data):
639 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700640 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800641
642 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
643 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700644
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700645 system_items.GetMetadata(input_zip)
646 system_items.Get("system").SetPermissions(script)
647
648 if HasVendorPartition(input_zip):
649 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
650 script.ShowProgress(0.1, 0)
651
652 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700653 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
654 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700655 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700656 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700657 else:
658 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700659 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700660 script.UnpackPackageDir("vendor", "/vendor")
661
662 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
663 script.MakeSymlinks(symlinks)
664
665 vendor_items.GetMetadata(input_zip)
666 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700667
Doug Zongker37974732010-09-16 17:44:38 -0700668 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700669 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700670
Doug Zongker01ce19c2014-02-04 13:48:15 -0800671 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700672 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700673
Doug Zongker01ce19c2014-02-04 13:48:15 -0800674 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700675 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700676
Doug Zongker1c390a22009-05-14 19:06:36 -0700677 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700678 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700679
Doug Zongker14833602010-02-02 13:12:04 -0800680 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800681
Doug Zongker922206e2014-03-04 13:16:24 -0800682 if OPTIONS.wipe_user_data:
683 script.ShowProgress(0.1, 10)
684 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700685
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800686 if OPTIONS.two_step:
687 script.AppendExtra("""
688set_stage("%(bcb_dev)s", "");
689""" % bcb_dev)
690 script.AppendExtra("else\n")
691 script.WriteRawImage("/boot", "recovery.img")
692 script.AppendExtra("""
693set_stage("%(bcb_dev)s", "2/3");
694reboot_now("%(bcb_dev)s", "");
695endif;
696endif;
697""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800698 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700699 WriteMetadata(metadata, output_zip)
700
Doug Zongkerfc44a512014-08-26 13:10:25 -0700701
Dan Albert8e0178d2015-01-27 15:53:15 -0800702def WritePolicyConfig(file_name, output_zip):
703 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500704
Doug Zongker2ea21062010-04-28 16:05:21 -0700705
706def WriteMetadata(metadata, output_zip):
707 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
708 "".join(["%s=%s\n" % kv
709 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700710
Doug Zongkerfc44a512014-08-26 13:10:25 -0700711
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700712def LoadPartitionFiles(z, partition):
713 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700714 ZipFile, and return a dict of {filename: File object}."""
715 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700716 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700717 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700718 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700719 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700720 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700721 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700722 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800723 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700724
725
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700726def GetBuildProp(prop, info_dict):
727 """Return the fingerprint of the build of a given target-files info_dict."""
728 try:
729 return info_dict.get("build.prop", {})[prop]
730 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700731 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700732
Doug Zongkerfc44a512014-08-26 13:10:25 -0700733
Michael Runge4038aa82013-12-13 18:06:28 -0800734def AddToKnownPaths(filename, known_paths):
735 if filename[-1] == "/":
736 return
737 dirs = filename.split("/")[:-1]
738 while len(dirs) > 0:
739 path = "/".join(dirs)
740 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700741 break
Michael Runge4038aa82013-12-13 18:06:28 -0800742 known_paths.add(path)
743 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700744
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700745
Geremy Condra36bd3652014-02-06 19:45:10 -0800746def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700747 # TODO(tbao): We should factor out the common parts between
748 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800749 source_version = OPTIONS.source_info_dict["recovery_api_version"]
750 target_version = OPTIONS.target_info_dict["recovery_api_version"]
751
752 if source_version == 0:
753 print ("WARNING: generating edify script for a source that "
754 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700755 script = edify_generator.EdifyGenerator(
756 source_version, OPTIONS.target_info_dict,
757 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800758
Tao Bao3806c232015-07-05 21:08:33 -0700759 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
760 recovery_mount_options = OPTIONS.source_info_dict.get(
761 "recovery_mount_options")
762 oem_dict = None
763 if oem_props is not None and len(oem_props) > 0:
764 if OPTIONS.oem_source is None:
765 raise common.ExternalError("OEM source required for this build")
766 script.Mount("/oem", recovery_mount_options)
767 oem_dict = common.LoadDictionaryFromLines(
768 open(OPTIONS.oem_source).readlines())
769
Dan Albert8b72aef2015-03-23 19:13:21 -0700770 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700771 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
772 OPTIONS.source_info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700773 "post-timestamp": GetBuildProp("ro.build.date.utc",
774 OPTIONS.target_info_dict),
775 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800776
777 device_specific = common.DeviceSpecificParams(
778 source_zip=source_zip,
779 source_version=source_version,
780 target_zip=target_zip,
781 target_version=target_version,
782 output_zip=output_zip,
783 script=script,
784 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700785 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800786
Tao Bao3806c232015-07-05 21:08:33 -0700787 source_fp = CalculateFingerprint(oem_props, oem_dict,
788 OPTIONS.source_info_dict)
789 target_fp = CalculateFingerprint(oem_props, oem_dict,
790 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800791 metadata["pre-build"] = source_fp
792 metadata["post-build"] = target_fp
793
794 source_boot = common.GetBootableImage(
795 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
796 OPTIONS.source_info_dict)
797 target_boot = common.GetBootableImage(
798 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
799 updating_boot = (not OPTIONS.two_step and
800 (source_boot.data != target_boot.data))
801
Geremy Condra36bd3652014-02-06 19:45:10 -0800802 target_recovery = common.GetBootableImage(
803 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800804
Doug Zongkerfc44a512014-08-26 13:10:25 -0700805 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
806 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700807
808 blockimgdiff_version = 1
809 if OPTIONS.info_dict:
810 blockimgdiff_version = max(
811 int(i) for i in
812 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
813
Tianjie Xufc3422a2015-12-15 11:53:59 -0800814 # Check first block of system partition for remount R/W only if
815 # disk type is ext4
816 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
817 check_first_block = system_partition.fs_type=="ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700818 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800819 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700820 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700821
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700822 if HasVendorPartition(target_zip):
823 if not HasVendorPartition(source_zip):
824 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700825 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
826 OPTIONS.source_info_dict)
827 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
828 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800829
830 # Check first block of vendor partition for remount R/W only if
831 # disk type is ext4
832 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
833 check_first_block = vendor_partition.fs_type=="ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700834 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800835 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700836 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700837 else:
838 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800839
Michael Rungec6e3afd2014-05-05 11:55:47 -0700840 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800841 device_specific.IncrementalOTA_Assertions()
842
843 # Two-step incremental package strategy (in chronological order,
844 # which is *not* the order in which the generated script has
845 # things):
846 #
847 # if stage is not "2/3" or "3/3":
848 # do verification on current system
849 # write recovery image to boot partition
850 # set stage to "2/3"
851 # reboot to boot partition and restart recovery
852 # else if stage is "2/3":
853 # write recovery image to recovery partition
854 # set stage to "3/3"
855 # reboot to recovery partition and restart recovery
856 # else:
857 # (stage must be "3/3")
858 # perform update:
859 # patch system files, etc.
860 # force full install of new boot image
861 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700862 # complete script normally
863 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800864
865 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700866 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800867 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700868 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800869 assert fs.fs_type.upper() == "EMMC", \
870 "two-step packages only supported on devices with EMMC /misc partitions"
871 bcb_dev = {"bcb_dev": fs.device}
872 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
873 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700874if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800875""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700876 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800877 script.WriteRawImage("/recovery", "recovery.img")
878 script.AppendExtra("""
879set_stage("%(bcb_dev)s", "3/3");
880reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700881else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800882""" % bcb_dev)
883
Tao Bao6c55a8a2015-04-08 15:30:27 -0700884 # Dump fingerprints
885 script.Print("Source: %s" % CalculateFingerprint(
886 oem_props, oem_dict, OPTIONS.source_info_dict))
887 script.Print("Target: %s" % CalculateFingerprint(
888 oem_props, oem_dict, OPTIONS.target_info_dict))
889
Geremy Condra36bd3652014-02-06 19:45:10 -0800890 script.Print("Verifying current system...")
891
892 device_specific.IncrementalOTA_VerifyBegin()
893
Michael Rungec6e3afd2014-05-05 11:55:47 -0700894 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700895 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
896 # patching on a device that's already on the target build will damage the
897 # system. Because operations like move don't check the block state, they
898 # always apply the changes unconditionally.
899 if blockimgdiff_version <= 2:
900 script.AssertSomeFingerprint(source_fp)
901 else:
902 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700903 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700904 if blockimgdiff_version <= 2:
905 script.AssertSomeThumbprint(
906 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
907 else:
908 script.AssertSomeThumbprint(
909 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
910 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800911
912 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700913 boot_type, boot_device = common.GetTypeAndDevice(
914 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800915 d = common.Difference(target_boot, source_boot)
916 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700917 if d is None:
918 include_full_boot = True
919 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
920 else:
921 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800922
Doug Zongkerf8340082014-08-05 10:39:37 -0700923 print "boot target: %d source: %d diff: %d" % (
924 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800925
Doug Zongkerf8340082014-08-05 10:39:37 -0700926 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800927
Doug Zongkerf8340082014-08-05 10:39:37 -0700928 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
929 (boot_type, boot_device,
930 source_boot.size, source_boot.sha1,
931 target_boot.size, target_boot.sha1))
Geremy Condra36bd3652014-02-06 19:45:10 -0800932
933 device_specific.IncrementalOTA_VerifyEnd()
934
935 if OPTIONS.two_step:
936 script.WriteRawImage("/boot", "recovery.img")
937 script.AppendExtra("""
938set_stage("%(bcb_dev)s", "2/3");
939reboot_now("%(bcb_dev)s", "");
940else
941""" % bcb_dev)
942
Jesse Zhao75bcea02015-01-06 10:59:53 -0800943 # Verify the existing partitions.
944 system_diff.WriteVerifyScript(script)
945 if vendor_diff:
946 vendor_diff.WriteVerifyScript(script)
947
Geremy Condra36bd3652014-02-06 19:45:10 -0800948 script.Comment("---- start making changes here ----")
949
950 device_specific.IncrementalOTA_InstallBegin()
951
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700952 system_diff.WriteScript(script, output_zip,
953 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700954
Doug Zongkerfc44a512014-08-26 13:10:25 -0700955 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700956 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800957
958 if OPTIONS.two_step:
959 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
960 script.WriteRawImage("/boot", "boot.img")
961 print "writing full boot image (forced by two-step mode)"
962
963 if not OPTIONS.two_step:
964 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700965 if include_full_boot:
966 print "boot image changed; including full."
967 script.Print("Installing boot image...")
968 script.WriteRawImage("/boot", "boot.img")
969 else:
970 # Produce the boot image by applying a patch to the current
971 # contents of the boot partition, and write it back to the
972 # partition.
973 print "boot image changed; including patch."
974 script.Print("Patching boot image...")
975 script.ShowProgress(0.1, 10)
976 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
977 % (boot_type, boot_device,
978 source_boot.size, source_boot.sha1,
979 target_boot.size, target_boot.sha1),
980 "-",
981 target_boot.size, target_boot.sha1,
982 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800983 else:
984 print "boot image unchanged; skipping."
985
986 # Do device-specific installation (eg, write radio image).
987 device_specific.IncrementalOTA_InstallEnd()
988
989 if OPTIONS.extra_script is not None:
990 script.AppendExtra(OPTIONS.extra_script)
991
Doug Zongker922206e2014-03-04 13:16:24 -0800992 if OPTIONS.wipe_user_data:
993 script.Print("Erasing user data...")
994 script.FormatPartition("/data")
995
Geremy Condra36bd3652014-02-06 19:45:10 -0800996 if OPTIONS.two_step:
997 script.AppendExtra("""
998set_stage("%(bcb_dev)s", "");
999endif;
1000endif;
1001""" % bcb_dev)
1002
1003 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -08001004 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -08001005 WriteMetadata(metadata, output_zip)
1006
Doug Zongker32b527d2014-03-04 10:03:02 -08001007
Tao Bao9bc6bb22015-11-09 16:58:28 -08001008def WriteVerifyPackage(input_zip, output_zip):
1009 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1010
1011 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1012 recovery_mount_options = OPTIONS.info_dict.get(
1013 "recovery_mount_options")
1014 oem_dict = None
1015 if oem_props is not None and len(oem_props) > 0:
1016 if OPTIONS.oem_source is None:
1017 raise common.ExternalError("OEM source required for this build")
1018 script.Mount("/oem", recovery_mount_options)
1019 oem_dict = common.LoadDictionaryFromLines(
1020 open(OPTIONS.oem_source).readlines())
1021
1022 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1023 metadata = {
1024 "post-build": target_fp,
1025 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1026 OPTIONS.info_dict),
1027 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1028 }
1029
1030 device_specific = common.DeviceSpecificParams(
1031 input_zip=input_zip,
1032 input_version=OPTIONS.info_dict["recovery_api_version"],
1033 output_zip=output_zip,
1034 script=script,
1035 input_tmp=OPTIONS.input_tmp,
1036 metadata=metadata,
1037 info_dict=OPTIONS.info_dict)
1038
1039 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1040
1041 script.Print("Verifying device images against %s..." % target_fp)
1042 script.AppendExtra("")
1043
1044 script.Print("Verifying boot...")
1045 boot_img = common.GetBootableImage(
1046 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1047 boot_type, boot_device = common.GetTypeAndDevice(
1048 "/boot", OPTIONS.info_dict)
1049 script.Verify("%s:%s:%d:%s" % (
1050 boot_type, boot_device, boot_img.size, boot_img.sha1))
1051 script.AppendExtra("")
1052
1053 script.Print("Verifying recovery...")
1054 recovery_img = common.GetBootableImage(
1055 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1056 recovery_type, recovery_device = common.GetTypeAndDevice(
1057 "/recovery", OPTIONS.info_dict)
1058 script.Verify("%s:%s:%d:%s" % (
1059 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1060 script.AppendExtra("")
1061
1062 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1063 system_tgt.ResetFileMap()
1064 system_diff = common.BlockDifference("system", system_tgt, src=None)
1065 system_diff.WriteStrictVerifyScript(script)
1066
1067 if HasVendorPartition(input_zip):
1068 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1069 vendor_tgt.ResetFileMap()
1070 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1071 vendor_diff.WriteStrictVerifyScript(script)
1072
1073 # Device specific partitions, such as radio, bootloader and etc.
1074 device_specific.VerifyOTA_Assertions()
1075
1076 script.SetProgress(1.0)
1077 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
1078 WriteMetadata(metadata, output_zip)
1079
1080
Dan Albert8b72aef2015-03-23 19:13:21 -07001081class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001082 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001083 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001084 print "Loading target..."
1085 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1086 print "Loading source..."
1087 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1088
1089 self.verbatim_targets = verbatim_targets = []
1090 self.patch_list = patch_list = []
1091 diffs = []
1092 self.renames = renames = {}
1093 known_paths = set()
1094 largest_source_size = 0
1095
1096 matching_file_cache = {}
1097 for fn, sf in source_data.items():
1098 assert fn == sf.name
1099 matching_file_cache["path:" + fn] = sf
1100 if fn in target_data.keys():
1101 AddToKnownPaths(fn, known_paths)
1102 # Only allow eligibility for filename/sha matching
1103 # if there isn't a perfect path match.
1104 if target_data.get(sf.name) is None:
1105 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1106 matching_file_cache["sha:" + sf.sha1] = sf
1107
1108 for fn in sorted(target_data.keys()):
1109 tf = target_data[fn]
1110 assert fn == tf.name
1111 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1112 if sf is not None and sf.name != tf.name:
1113 print "File has moved from " + sf.name + " to " + tf.name
1114 renames[sf.name] = tf
1115
1116 if sf is None or fn in OPTIONS.require_verbatim:
1117 # This file should be included verbatim
1118 if fn in OPTIONS.prohibit_verbatim:
1119 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1120 print "send", fn, "verbatim"
1121 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001122 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001123 if fn in target_data.keys():
1124 AddToKnownPaths(fn, known_paths)
1125 elif tf.sha1 != sf.sha1:
1126 # File is different; consider sending as a patch
1127 diffs.append(common.Difference(tf, sf))
1128 else:
1129 # Target file data identical to source (may still be renamed)
1130 pass
1131
1132 common.ComputeDifferences(diffs)
1133
1134 for diff in diffs:
1135 tf, sf, d = diff.GetPatch()
1136 path = "/".join(tf.name.split("/")[:-1])
1137 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1138 path not in known_paths:
1139 # patch is almost as big as the file; don't bother patching
1140 # or a patch + rename cannot take place due to the target
1141 # directory not existing
1142 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001143 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001144 if sf.name in renames:
1145 del renames[sf.name]
1146 AddToKnownPaths(tf.name, known_paths)
1147 else:
1148 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1149 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1150 largest_source_size = max(largest_source_size, sf.size)
1151
1152 self.largest_source_size = largest_source_size
1153
1154 def EmitVerification(self, script):
1155 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001156 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001157 if tf.name != sf.name:
1158 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1159 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1160 so_far += sf.size
1161 return so_far
1162
Michael Runge63f01de2014-10-28 19:24:19 -07001163 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001164 for fn, _, sha1 in self.verbatim_targets:
1165 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001166 script.FileCheck("/"+fn, sha1)
1167 for tf, _, _, _ in self.patch_list:
1168 script.FileCheck(tf.name, tf.sha1)
1169
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001170 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001171 file_list = ["/" + i[0] for i in self.verbatim_targets]
1172 file_list += ["/" + i for i in self.source_data
1173 if i not in self.target_data and i not in self.renames]
1174 file_list += list(extras)
1175 # Sort the list in descending order, which removes all the files first
1176 # before attempting to remove the folder. (Bug: 22960996)
1177 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001178
1179 def TotalPatchSize(self):
1180 return sum(i[1].size for i in self.patch_list)
1181
1182 def EmitPatches(self, script, total_patch_size, so_far):
1183 self.deferred_patch_list = deferred_patch_list = []
1184 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001185 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001186 if tf.name == "system/build.prop":
1187 deferred_patch_list.append(item)
1188 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001189 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001190 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001191 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1192 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001193 so_far += tf.size
1194 script.SetProgress(so_far / total_patch_size)
1195 return so_far
1196
1197 def EmitDeferredPatches(self, script):
1198 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001199 tf, sf, _, _ = item
1200 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1201 "patch/" + sf.name + ".p")
1202 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001203
1204 def EmitRenames(self, script):
1205 if len(self.renames) > 0:
1206 script.Print("Renaming files...")
1207 for src, tgt in self.renames.iteritems():
1208 print "Renaming " + src + " to " + tgt.name
1209 script.RenameFile(src, tgt.name)
1210
1211
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001212def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001213 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1214 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1215
Doug Zongker26e66192014-02-20 13:22:07 -08001216 if (OPTIONS.block_based and
1217 target_has_recovery_patch and
1218 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001219 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1220
Doug Zongker37974732010-09-16 17:44:38 -07001221 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1222 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001223
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001224 if source_version == 0:
1225 print ("WARNING: generating edify script for a source that "
1226 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001227 script = edify_generator.EdifyGenerator(
1228 source_version, OPTIONS.target_info_dict,
1229 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001230
Michael Runge6e836112014-04-15 17:40:21 -07001231 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001232 recovery_mount_options = OPTIONS.source_info_dict.get(
1233 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001234 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001235 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001236 if OPTIONS.oem_source is None:
1237 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -07001238 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001239 oem_dict = common.LoadDictionaryFromLines(
1240 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001241
Dan Albert8b72aef2015-03-23 19:13:21 -07001242 metadata = {
1243 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1244 OPTIONS.source_info_dict),
1245 "post-timestamp": GetBuildProp("ro.build.date.utc",
1246 OPTIONS.target_info_dict),
1247 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001248
Doug Zongker05d3dea2009-06-22 11:32:31 -07001249 device_specific = common.DeviceSpecificParams(
1250 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001251 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001252 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001253 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001254 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001255 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001256 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001257 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001258
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001259 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001260 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001261 if HasVendorPartition(target_zip):
1262 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001263 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001264 else:
1265 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001266
Dan Albert8b72aef2015-03-23 19:13:21 -07001267 target_fp = CalculateFingerprint(oem_props, oem_dict,
1268 OPTIONS.target_info_dict)
1269 source_fp = CalculateFingerprint(oem_props, oem_dict,
1270 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001271
1272 if oem_props is None:
1273 script.AssertSomeFingerprint(source_fp, target_fp)
1274 else:
1275 script.AssertSomeThumbprint(
1276 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1277 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1278
Doug Zongker2ea21062010-04-28 16:05:21 -07001279 metadata["pre-build"] = source_fp
1280 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001281
Doug Zongker55d93282011-01-25 17:03:34 -08001282 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001283 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1284 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001285 target_boot = common.GetBootableImage(
1286 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001287 updating_boot = (not OPTIONS.two_step and
1288 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001289
Doug Zongker55d93282011-01-25 17:03:34 -08001290 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001291 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1292 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001293 target_recovery = common.GetBootableImage(
1294 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001295 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001296
Doug Zongker881dd402009-09-20 14:03:55 -07001297 # Here's how we divide up the progress bar:
1298 # 0.1 for verifying the start state (PatchCheck calls)
1299 # 0.8 for applying patches (ApplyPatch calls)
1300 # 0.1 for unpacking verbatim files, symlinking, and doing the
1301 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001302
Michael Runge6e836112014-04-15 17:40:21 -07001303 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001304 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001305
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001306 # Two-step incremental package strategy (in chronological order,
1307 # which is *not* the order in which the generated script has
1308 # things):
1309 #
1310 # if stage is not "2/3" or "3/3":
1311 # do verification on current system
1312 # write recovery image to boot partition
1313 # set stage to "2/3"
1314 # reboot to boot partition and restart recovery
1315 # else if stage is "2/3":
1316 # write recovery image to recovery partition
1317 # set stage to "3/3"
1318 # reboot to recovery partition and restart recovery
1319 # else:
1320 # (stage must be "3/3")
1321 # perform update:
1322 # patch system files, etc.
1323 # force full install of new boot image
1324 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001325 # complete script normally
1326 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001327
1328 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001329 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001330 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001331 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001332 assert fs.fs_type.upper() == "EMMC", \
1333 "two-step packages only supported on devices with EMMC /misc partitions"
1334 bcb_dev = {"bcb_dev": fs.device}
1335 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1336 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001337if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001338""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001339 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001340 script.WriteRawImage("/recovery", "recovery.img")
1341 script.AppendExtra("""
1342set_stage("%(bcb_dev)s", "3/3");
1343reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001344else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001345""" % bcb_dev)
1346
Tao Bao6c55a8a2015-04-08 15:30:27 -07001347 # Dump fingerprints
1348 script.Print("Source: %s" % (source_fp,))
1349 script.Print("Target: %s" % (target_fp,))
1350
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001351 script.Print("Verifying current system...")
1352
Doug Zongkere5ff5902012-01-17 10:55:37 -08001353 device_specific.IncrementalOTA_VerifyBegin()
1354
Doug Zongker881dd402009-09-20 14:03:55 -07001355 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001356 so_far = system_diff.EmitVerification(script)
1357 if vendor_diff:
1358 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001359
Doug Zongker5da317e2009-06-02 13:38:17 -07001360 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001361 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001362 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001363 print "boot target: %d source: %d diff: %d" % (
1364 target_boot.size, source_boot.size, len(d))
1365
Doug Zongker048e7ca2009-06-15 14:31:53 -07001366 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001367
Tao Baodd24da92015-07-29 14:09:23 -07001368 boot_type, boot_device = common.GetTypeAndDevice(
1369 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001370
1371 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1372 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001373 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001374 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001375 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001376
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001377 size = []
Dan Albert8b72aef2015-03-23 19:13:21 -07001378 if system_diff.patch_list:
1379 size.append(system_diff.largest_source_size)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001380 if vendor_diff:
Dan Albert8b72aef2015-03-23 19:13:21 -07001381 if vendor_diff.patch_list:
1382 size.append(vendor_diff.largest_source_size)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001383 if size or updating_recovery or updating_boot:
1384 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001385
Doug Zongker05d3dea2009-06-22 11:32:31 -07001386 device_specific.IncrementalOTA_VerifyEnd()
1387
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001388 if OPTIONS.two_step:
1389 script.WriteRawImage("/boot", "recovery.img")
1390 script.AppendExtra("""
1391set_stage("%(bcb_dev)s", "2/3");
1392reboot_now("%(bcb_dev)s", "");
1393else
1394""" % bcb_dev)
1395
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001396 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001397
Doug Zongkere5ff5902012-01-17 10:55:37 -08001398 device_specific.IncrementalOTA_InstallBegin()
1399
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001400 if OPTIONS.two_step:
1401 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1402 script.WriteRawImage("/boot", "boot.img")
1403 print "writing full boot image (forced by two-step mode)"
1404
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001405 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001406 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1407 if vendor_diff:
1408 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001409
Doug Zongker881dd402009-09-20 14:03:55 -07001410 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001411 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1412 if vendor_diff:
1413 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001414 if updating_boot:
1415 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001416
1417 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001418 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1419 if vendor_diff:
1420 script.Print("Patching vendor files...")
1421 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001422
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001423 if not OPTIONS.two_step:
1424 if updating_boot:
1425 # Produce the boot image by applying a patch to the current
1426 # contents of the boot partition, and write it back to the
1427 # partition.
1428 script.Print("Patching boot image...")
1429 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1430 % (boot_type, boot_device,
1431 source_boot.size, source_boot.sha1,
1432 target_boot.size, target_boot.sha1),
1433 "-",
1434 target_boot.size, target_boot.sha1,
1435 source_boot.sha1, "patch/boot.img.p")
1436 so_far += target_boot.size
1437 script.SetProgress(so_far / total_patch_size)
1438 print "boot image changed; including."
1439 else:
1440 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001441
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001442 system_items = ItemSet("system", "META/filesystem_config.txt")
1443 if vendor_diff:
1444 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1445
Doug Zongkereef39442009-04-02 12:14:19 -07001446 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001447 # Recovery is generated as a patch using both the boot image
1448 # (which contains the same linux kernel as recovery) and the file
1449 # /system/etc/recovery-resource.dat (which contains all the images
1450 # used in the recovery UI) as sources. This lets us minimize the
1451 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001452 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001453 # For older builds where recovery-resource.dat is not present, we
1454 # use only the boot image as the source.
1455
Doug Zongkerc9253822014-02-04 12:17:58 -08001456 if not target_has_recovery_patch:
1457 def output_sink(fn, data):
1458 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001459 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001460
1461 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1462 target_recovery, target_boot)
1463 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001464 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001465 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001466 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001467 else:
1468 print "recovery image unchanged; skipping."
1469
Doug Zongker881dd402009-09-20 14:03:55 -07001470 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001471
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001472 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1473 if vendor_diff:
1474 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1475
1476 temp_script = script.MakeTemporary()
1477 system_items.GetMetadata(target_zip)
1478 system_items.Get("system").SetPermissions(temp_script)
1479 if vendor_diff:
1480 vendor_items.GetMetadata(target_zip)
1481 vendor_items.Get("vendor").SetPermissions(temp_script)
1482
1483 # Note that this call will mess up the trees of Items, so make sure
1484 # we're done with them.
1485 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1486 if vendor_diff:
1487 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001488
1489 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001490 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1491
1492 # Delete all the symlinks in source that aren't in target. This
1493 # needs to happen before verbatim files are unpacked, in case a
1494 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001495
1496 # If a symlink in the source will be replaced by a regular file, we cannot
1497 # delete the symlink/file in case the package gets applied again. For such
1498 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1499 # (Bug: 23646151)
1500 replaced_symlinks = dict()
1501 if system_diff:
1502 for i in system_diff.verbatim_targets:
1503 replaced_symlinks["/%s" % (i[0],)] = i[2]
1504 if vendor_diff:
1505 for i in vendor_diff.verbatim_targets:
1506 replaced_symlinks["/%s" % (i[0],)] = i[2]
1507
1508 if system_diff:
1509 for tf in system_diff.renames.values():
1510 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1511 if vendor_diff:
1512 for tf in vendor_diff.renames.values():
1513 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1514
1515 always_delete = []
1516 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001517 for dest, link in source_symlinks:
1518 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001519 if link in replaced_symlinks:
1520 may_delete.append((link, replaced_symlinks[link]))
1521 else:
1522 always_delete.append(link)
1523 script.DeleteFiles(always_delete)
1524 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001525
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001526 if system_diff.verbatim_targets:
1527 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001528 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001529 if vendor_diff and vendor_diff.verbatim_targets:
1530 script.Print("Unpacking new vendor files...")
1531 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001532
Doug Zongkerc9253822014-02-04 12:17:58 -08001533 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001534 script.Print("Unpacking new recovery...")
1535 script.UnpackPackageDir("recovery", "/system")
1536
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001537 system_diff.EmitRenames(script)
1538 if vendor_diff:
1539 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001540
Doug Zongker05d3dea2009-06-22 11:32:31 -07001541 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001542
1543 # Create all the symlinks that don't already exist, or point to
1544 # somewhere different than what we want. Delete each symlink before
1545 # creating it, since the 'symlink' command won't overwrite.
1546 to_create = []
1547 for dest, link in target_symlinks:
1548 if link in source_symlinks_d:
1549 if dest != source_symlinks_d[link]:
1550 to_create.append((dest, link))
1551 else:
1552 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001553 script.DeleteFiles([i[1] for i in to_create])
1554 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001555
1556 # Now that the symlinks are created, we can set all the
1557 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001558 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001559
Doug Zongker881dd402009-09-20 14:03:55 -07001560 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001561 device_specific.IncrementalOTA_InstallEnd()
1562
Doug Zongker1c390a22009-05-14 19:06:36 -07001563 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001564 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001565
Doug Zongkere92f15a2011-08-26 13:46:40 -07001566 # Patch the build.prop file last, so if something fails but the
1567 # device can still come up, it appears to be the old build and will
1568 # get set the OTA package again to retry.
1569 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001570 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001571
Doug Zongker922206e2014-03-04 13:16:24 -08001572 if OPTIONS.wipe_user_data:
1573 script.Print("Erasing user data...")
1574 script.FormatPartition("/data")
1575
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001576 if OPTIONS.two_step:
1577 script.AppendExtra("""
1578set_stage("%(bcb_dev)s", "");
1579endif;
1580endif;
1581""" % bcb_dev)
1582
Michael Runge63f01de2014-10-28 19:24:19 -07001583 if OPTIONS.verify and system_diff:
1584 script.Print("Remounting and verifying system partition files...")
1585 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001586 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001587 system_diff.EmitExplicitTargetVerification(script)
1588
1589 if OPTIONS.verify and vendor_diff:
1590 script.Print("Remounting and verifying vendor partition files...")
1591 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001592 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001593 vendor_diff.EmitExplicitTargetVerification(script)
Doug Zongker25568482014-03-03 10:21:27 -08001594 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001595
Doug Zongker2ea21062010-04-28 16:05:21 -07001596 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001597
1598
1599def main(argv):
1600
1601 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001602 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001603 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001604 elif o in ("-k", "--package_key"):
1605 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001606 elif o in ("-i", "--incremental_from"):
1607 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001608 elif o == "--full_radio":
1609 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001610 elif o == "--full_bootloader":
1611 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001612 elif o in ("-w", "--wipe_user_data"):
1613 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001614 elif o in ("-n", "--no_prereq"):
1615 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001616 elif o in ("-o", "--oem_settings"):
1617 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001618 elif o in ("-e", "--extra_script"):
1619 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001620 elif o in ("-a", "--aslr_mode"):
1621 if a in ("on", "On", "true", "True", "yes", "Yes"):
1622 OPTIONS.aslr_mode = True
1623 else:
1624 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001625 elif o in ("-t", "--worker_threads"):
1626 if a.isdigit():
1627 OPTIONS.worker_threads = int(a)
1628 else:
1629 raise ValueError("Cannot parse value %r for option %r - only "
1630 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001631 elif o in ("-2", "--two_step"):
1632 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001633 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001634 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001635 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001636 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001637 elif o == "--block":
1638 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001639 elif o in ("-b", "--binary"):
1640 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001641 elif o in ("--no_fallback_to_full",):
1642 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001643 elif o == "--stash_threshold":
1644 try:
1645 OPTIONS.stash_threshold = float(a)
1646 except ValueError:
1647 raise ValueError("Cannot parse value %r for option %r - expecting "
1648 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001649 elif o == "--gen_verify":
1650 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001651 elif o == "--log_diff":
1652 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001653 else:
1654 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001655 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001656
1657 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001658 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001659 extra_long_opts=[
1660 "board_config=",
1661 "package_key=",
1662 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001663 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001664 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001665 "wipe_user_data",
1666 "no_prereq",
1667 "extra_script=",
1668 "worker_threads=",
1669 "aslr_mode=",
1670 "two_step",
1671 "no_signing",
1672 "block",
1673 "binary=",
1674 "oem_settings=",
1675 "verify",
1676 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001677 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001678 "gen_verify",
1679 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001680 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001681
1682 if len(args) != 2:
1683 common.Usage(__doc__)
1684 sys.exit(1)
1685
Doug Zongker1c390a22009-05-14 19:06:36 -07001686 if OPTIONS.extra_script is not None:
1687 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1688
Doug Zongkereef39442009-04-02 12:14:19 -07001689 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001690 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001691
Doug Zongkereef39442009-04-02 12:14:19 -07001692 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001693 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001694
Doug Zongker37974732010-09-16 17:44:38 -07001695 if OPTIONS.verbose:
1696 print "--- target info ---"
1697 common.DumpInfoDict(OPTIONS.info_dict)
1698
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001699 # If the caller explicitly specified the device-specific extensions
1700 # path via -s/--device_specific, use that. Otherwise, use
1701 # META/releasetools.py if it is present in the target target_files.
1702 # Otherwise, take the path of the file from 'tool_extensions' in the
1703 # info dict and look for that in the local filesystem, relative to
1704 # the current directory.
1705
Doug Zongker37974732010-09-16 17:44:38 -07001706 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001707 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1708 if os.path.exists(from_input):
1709 print "(using device-specific extensions from target_files)"
1710 OPTIONS.device_specific = from_input
1711 else:
1712 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1713
Doug Zongker37974732010-09-16 17:44:38 -07001714 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001715 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001716
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001717 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1718
1719 if OPTIONS.info_dict.get("no_recovery") == "true" and not ab_update:
Tao Baodb45efa2015-10-27 19:25:18 -07001720 raise common.ExternalError(
1721 "--- target build has specified no recovery ---")
1722
Tao Bao767e3ac2015-11-10 12:19:19 -08001723 # Use the default key to sign the package if not specified with package_key.
1724 if not OPTIONS.no_signing:
1725 if OPTIONS.package_key is None:
1726 OPTIONS.package_key = OPTIONS.info_dict.get(
1727 "default_system_dev_certificate",
1728 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001729
Tao Bao767e3ac2015-11-10 12:19:19 -08001730 # Set up the output zip. Create a temporary zip file if signing is needed.
1731 if OPTIONS.no_signing:
1732 if os.path.exists(args[1]):
1733 os.unlink(args[1])
1734 output_zip = zipfile.ZipFile(args[1], "w",
1735 compression=zipfile.ZIP_DEFLATED)
1736 else:
1737 temp_zip_file = tempfile.NamedTemporaryFile()
1738 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1739 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001740
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001741 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001742 cache_size = OPTIONS.info_dict.get("cache_size", None)
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001743 if cache_size is None and not ab_update:
Tao Bao767e3ac2015-11-10 12:19:19 -08001744 print "--- can't determine the cache partition size ---"
1745 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001746
Tao Bao9bc6bb22015-11-09 16:58:28 -08001747 # Generate a verify package.
1748 if OPTIONS.gen_verify:
1749 WriteVerifyPackage(input_zip, output_zip)
1750
Tao Bao767e3ac2015-11-10 12:19:19 -08001751 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001752 elif OPTIONS.incremental_source is None:
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001753 if ab_update:
1754 # TODO: Pending for b/25715402.
1755 pass
1756 else:
1757 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001758
1759 # Generate an incremental OTA. It will fall back to generate a full OTA on
1760 # failure unless no_fallback_to_full is specified.
1761 else:
1762 print "unzipping source target-files..."
1763 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
1764 OPTIONS.incremental_source)
1765 OPTIONS.target_info_dict = OPTIONS.info_dict
1766 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1767 OPTIONS.source_tmp)
1768 if OPTIONS.verbose:
1769 print "--- source info ---"
1770 common.DumpInfoDict(OPTIONS.source_info_dict)
1771 try:
1772 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001773 if OPTIONS.log_diff:
1774 out_file = open(OPTIONS.log_diff, 'w')
1775 import target_files_diff
1776 target_files_diff.recursiveDiff('',
1777 OPTIONS.source_tmp,
1778 OPTIONS.input_tmp,
1779 out_file)
1780 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001781 except ValueError:
1782 if not OPTIONS.fallback_to_full:
1783 raise
1784 print "--- failed to build incremental; falling back to full ---"
1785 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001786 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001787
Tao Bao767e3ac2015-11-10 12:19:19 -08001788 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001789
Tao Bao767e3ac2015-11-10 12:19:19 -08001790 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001791 if not OPTIONS.no_signing:
1792 SignOutput(temp_zip_file.name, args[1])
1793 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001794
Doug Zongkereef39442009-04-02 12:14:19 -07001795 print "done."
1796
1797
1798if __name__ == '__main__':
1799 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001800 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001801 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001802 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07001803 print
1804 print " ERROR: %s" % (e,)
1805 print
1806 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001807 finally:
1808 common.Cleanup()