blob: 5259ede59e2bb8ea5c3f33af6c0c685c21569bdc [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
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700814 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tao Baodd2a5892015-03-12 12:32:37 -0700815 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700816
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700817 if HasVendorPartition(target_zip):
818 if not HasVendorPartition(source_zip):
819 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700820 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
821 OPTIONS.source_info_dict)
822 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
823 OPTIONS.target_info_dict)
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700824 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tao Baodd2a5892015-03-12 12:32:37 -0700825 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700826 else:
827 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800828
Michael Rungec6e3afd2014-05-05 11:55:47 -0700829 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800830 device_specific.IncrementalOTA_Assertions()
831
832 # Two-step incremental package strategy (in chronological order,
833 # which is *not* the order in which the generated script has
834 # things):
835 #
836 # if stage is not "2/3" or "3/3":
837 # do verification on current system
838 # write recovery image to boot partition
839 # set stage to "2/3"
840 # reboot to boot partition and restart recovery
841 # else if stage is "2/3":
842 # write recovery image to recovery partition
843 # set stage to "3/3"
844 # reboot to recovery partition and restart recovery
845 # else:
846 # (stage must be "3/3")
847 # perform update:
848 # patch system files, etc.
849 # force full install of new boot image
850 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700851 # complete script normally
852 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800853
854 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700855 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800856 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700857 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800858 assert fs.fs_type.upper() == "EMMC", \
859 "two-step packages only supported on devices with EMMC /misc partitions"
860 bcb_dev = {"bcb_dev": fs.device}
861 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
862 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700863if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800864""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700865 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800866 script.WriteRawImage("/recovery", "recovery.img")
867 script.AppendExtra("""
868set_stage("%(bcb_dev)s", "3/3");
869reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700870else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800871""" % bcb_dev)
872
Tao Bao6c55a8a2015-04-08 15:30:27 -0700873 # Dump fingerprints
874 script.Print("Source: %s" % CalculateFingerprint(
875 oem_props, oem_dict, OPTIONS.source_info_dict))
876 script.Print("Target: %s" % CalculateFingerprint(
877 oem_props, oem_dict, OPTIONS.target_info_dict))
878
Geremy Condra36bd3652014-02-06 19:45:10 -0800879 script.Print("Verifying current system...")
880
881 device_specific.IncrementalOTA_VerifyBegin()
882
Michael Rungec6e3afd2014-05-05 11:55:47 -0700883 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700884 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
885 # patching on a device that's already on the target build will damage the
886 # system. Because operations like move don't check the block state, they
887 # always apply the changes unconditionally.
888 if blockimgdiff_version <= 2:
889 script.AssertSomeFingerprint(source_fp)
890 else:
891 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700892 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700893 if blockimgdiff_version <= 2:
894 script.AssertSomeThumbprint(
895 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
896 else:
897 script.AssertSomeThumbprint(
898 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
899 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800900
901 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700902 boot_type, boot_device = common.GetTypeAndDevice(
903 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800904 d = common.Difference(target_boot, source_boot)
905 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700906 if d is None:
907 include_full_boot = True
908 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
909 else:
910 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800911
Doug Zongkerf8340082014-08-05 10:39:37 -0700912 print "boot target: %d source: %d diff: %d" % (
913 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800914
Doug Zongkerf8340082014-08-05 10:39:37 -0700915 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800916
Doug Zongkerf8340082014-08-05 10:39:37 -0700917 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
918 (boot_type, boot_device,
919 source_boot.size, source_boot.sha1,
920 target_boot.size, target_boot.sha1))
Geremy Condra36bd3652014-02-06 19:45:10 -0800921
922 device_specific.IncrementalOTA_VerifyEnd()
923
924 if OPTIONS.two_step:
925 script.WriteRawImage("/boot", "recovery.img")
926 script.AppendExtra("""
927set_stage("%(bcb_dev)s", "2/3");
928reboot_now("%(bcb_dev)s", "");
929else
930""" % bcb_dev)
931
Jesse Zhao75bcea02015-01-06 10:59:53 -0800932 # Verify the existing partitions.
933 system_diff.WriteVerifyScript(script)
934 if vendor_diff:
935 vendor_diff.WriteVerifyScript(script)
936
Geremy Condra36bd3652014-02-06 19:45:10 -0800937 script.Comment("---- start making changes here ----")
938
939 device_specific.IncrementalOTA_InstallBegin()
940
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700941 system_diff.WriteScript(script, output_zip,
942 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700943
Doug Zongkerfc44a512014-08-26 13:10:25 -0700944 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700945 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800946
947 if OPTIONS.two_step:
948 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
949 script.WriteRawImage("/boot", "boot.img")
950 print "writing full boot image (forced by two-step mode)"
951
952 if not OPTIONS.two_step:
953 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700954 if include_full_boot:
955 print "boot image changed; including full."
956 script.Print("Installing boot image...")
957 script.WriteRawImage("/boot", "boot.img")
958 else:
959 # Produce the boot image by applying a patch to the current
960 # contents of the boot partition, and write it back to the
961 # partition.
962 print "boot image changed; including patch."
963 script.Print("Patching boot image...")
964 script.ShowProgress(0.1, 10)
965 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
966 % (boot_type, boot_device,
967 source_boot.size, source_boot.sha1,
968 target_boot.size, target_boot.sha1),
969 "-",
970 target_boot.size, target_boot.sha1,
971 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800972 else:
973 print "boot image unchanged; skipping."
974
975 # Do device-specific installation (eg, write radio image).
976 device_specific.IncrementalOTA_InstallEnd()
977
978 if OPTIONS.extra_script is not None:
979 script.AppendExtra(OPTIONS.extra_script)
980
Doug Zongker922206e2014-03-04 13:16:24 -0800981 if OPTIONS.wipe_user_data:
982 script.Print("Erasing user data...")
983 script.FormatPartition("/data")
984
Geremy Condra36bd3652014-02-06 19:45:10 -0800985 if OPTIONS.two_step:
986 script.AppendExtra("""
987set_stage("%(bcb_dev)s", "");
988endif;
989endif;
990""" % bcb_dev)
991
992 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800993 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800994 WriteMetadata(metadata, output_zip)
995
Doug Zongker32b527d2014-03-04 10:03:02 -0800996
Tao Bao9bc6bb22015-11-09 16:58:28 -0800997def WriteVerifyPackage(input_zip, output_zip):
998 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
999
1000 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1001 recovery_mount_options = OPTIONS.info_dict.get(
1002 "recovery_mount_options")
1003 oem_dict = None
1004 if oem_props is not None and len(oem_props) > 0:
1005 if OPTIONS.oem_source is None:
1006 raise common.ExternalError("OEM source required for this build")
1007 script.Mount("/oem", recovery_mount_options)
1008 oem_dict = common.LoadDictionaryFromLines(
1009 open(OPTIONS.oem_source).readlines())
1010
1011 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1012 metadata = {
1013 "post-build": target_fp,
1014 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1015 OPTIONS.info_dict),
1016 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1017 }
1018
1019 device_specific = common.DeviceSpecificParams(
1020 input_zip=input_zip,
1021 input_version=OPTIONS.info_dict["recovery_api_version"],
1022 output_zip=output_zip,
1023 script=script,
1024 input_tmp=OPTIONS.input_tmp,
1025 metadata=metadata,
1026 info_dict=OPTIONS.info_dict)
1027
1028 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1029
1030 script.Print("Verifying device images against %s..." % target_fp)
1031 script.AppendExtra("")
1032
1033 script.Print("Verifying boot...")
1034 boot_img = common.GetBootableImage(
1035 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1036 boot_type, boot_device = common.GetTypeAndDevice(
1037 "/boot", OPTIONS.info_dict)
1038 script.Verify("%s:%s:%d:%s" % (
1039 boot_type, boot_device, boot_img.size, boot_img.sha1))
1040 script.AppendExtra("")
1041
1042 script.Print("Verifying recovery...")
1043 recovery_img = common.GetBootableImage(
1044 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1045 recovery_type, recovery_device = common.GetTypeAndDevice(
1046 "/recovery", OPTIONS.info_dict)
1047 script.Verify("%s:%s:%d:%s" % (
1048 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1049 script.AppendExtra("")
1050
1051 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1052 system_tgt.ResetFileMap()
1053 system_diff = common.BlockDifference("system", system_tgt, src=None)
1054 system_diff.WriteStrictVerifyScript(script)
1055
1056 if HasVendorPartition(input_zip):
1057 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1058 vendor_tgt.ResetFileMap()
1059 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1060 vendor_diff.WriteStrictVerifyScript(script)
1061
1062 # Device specific partitions, such as radio, bootloader and etc.
1063 device_specific.VerifyOTA_Assertions()
1064
1065 script.SetProgress(1.0)
1066 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
1067 WriteMetadata(metadata, output_zip)
1068
1069
Dan Albert8b72aef2015-03-23 19:13:21 -07001070class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001071 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001072 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001073 print "Loading target..."
1074 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1075 print "Loading source..."
1076 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1077
1078 self.verbatim_targets = verbatim_targets = []
1079 self.patch_list = patch_list = []
1080 diffs = []
1081 self.renames = renames = {}
1082 known_paths = set()
1083 largest_source_size = 0
1084
1085 matching_file_cache = {}
1086 for fn, sf in source_data.items():
1087 assert fn == sf.name
1088 matching_file_cache["path:" + fn] = sf
1089 if fn in target_data.keys():
1090 AddToKnownPaths(fn, known_paths)
1091 # Only allow eligibility for filename/sha matching
1092 # if there isn't a perfect path match.
1093 if target_data.get(sf.name) is None:
1094 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1095 matching_file_cache["sha:" + sf.sha1] = sf
1096
1097 for fn in sorted(target_data.keys()):
1098 tf = target_data[fn]
1099 assert fn == tf.name
1100 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1101 if sf is not None and sf.name != tf.name:
1102 print "File has moved from " + sf.name + " to " + tf.name
1103 renames[sf.name] = tf
1104
1105 if sf is None or fn in OPTIONS.require_verbatim:
1106 # This file should be included verbatim
1107 if fn in OPTIONS.prohibit_verbatim:
1108 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1109 print "send", fn, "verbatim"
1110 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001111 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001112 if fn in target_data.keys():
1113 AddToKnownPaths(fn, known_paths)
1114 elif tf.sha1 != sf.sha1:
1115 # File is different; consider sending as a patch
1116 diffs.append(common.Difference(tf, sf))
1117 else:
1118 # Target file data identical to source (may still be renamed)
1119 pass
1120
1121 common.ComputeDifferences(diffs)
1122
1123 for diff in diffs:
1124 tf, sf, d = diff.GetPatch()
1125 path = "/".join(tf.name.split("/")[:-1])
1126 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1127 path not in known_paths:
1128 # patch is almost as big as the file; don't bother patching
1129 # or a patch + rename cannot take place due to the target
1130 # directory not existing
1131 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001132 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001133 if sf.name in renames:
1134 del renames[sf.name]
1135 AddToKnownPaths(tf.name, known_paths)
1136 else:
1137 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1138 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1139 largest_source_size = max(largest_source_size, sf.size)
1140
1141 self.largest_source_size = largest_source_size
1142
1143 def EmitVerification(self, script):
1144 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001145 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001146 if tf.name != sf.name:
1147 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1148 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1149 so_far += sf.size
1150 return so_far
1151
Michael Runge63f01de2014-10-28 19:24:19 -07001152 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001153 for fn, _, sha1 in self.verbatim_targets:
1154 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001155 script.FileCheck("/"+fn, sha1)
1156 for tf, _, _, _ in self.patch_list:
1157 script.FileCheck(tf.name, tf.sha1)
1158
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001159 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001160 file_list = ["/" + i[0] for i in self.verbatim_targets]
1161 file_list += ["/" + i for i in self.source_data
1162 if i not in self.target_data and i not in self.renames]
1163 file_list += list(extras)
1164 # Sort the list in descending order, which removes all the files first
1165 # before attempting to remove the folder. (Bug: 22960996)
1166 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001167
1168 def TotalPatchSize(self):
1169 return sum(i[1].size for i in self.patch_list)
1170
1171 def EmitPatches(self, script, total_patch_size, so_far):
1172 self.deferred_patch_list = deferred_patch_list = []
1173 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001174 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001175 if tf.name == "system/build.prop":
1176 deferred_patch_list.append(item)
1177 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001178 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001179 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001180 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1181 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001182 so_far += tf.size
1183 script.SetProgress(so_far / total_patch_size)
1184 return so_far
1185
1186 def EmitDeferredPatches(self, script):
1187 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001188 tf, sf, _, _ = item
1189 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1190 "patch/" + sf.name + ".p")
1191 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001192
1193 def EmitRenames(self, script):
1194 if len(self.renames) > 0:
1195 script.Print("Renaming files...")
1196 for src, tgt in self.renames.iteritems():
1197 print "Renaming " + src + " to " + tgt.name
1198 script.RenameFile(src, tgt.name)
1199
1200
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001201def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001202 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1203 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1204
Doug Zongker26e66192014-02-20 13:22:07 -08001205 if (OPTIONS.block_based and
1206 target_has_recovery_patch and
1207 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001208 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1209
Doug Zongker37974732010-09-16 17:44:38 -07001210 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1211 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001212
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001213 if source_version == 0:
1214 print ("WARNING: generating edify script for a source that "
1215 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001216 script = edify_generator.EdifyGenerator(
1217 source_version, OPTIONS.target_info_dict,
1218 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001219
Michael Runge6e836112014-04-15 17:40:21 -07001220 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001221 recovery_mount_options = OPTIONS.source_info_dict.get(
1222 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001223 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001224 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001225 if OPTIONS.oem_source is None:
1226 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -07001227 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001228 oem_dict = common.LoadDictionaryFromLines(
1229 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001230
Dan Albert8b72aef2015-03-23 19:13:21 -07001231 metadata = {
1232 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1233 OPTIONS.source_info_dict),
1234 "post-timestamp": GetBuildProp("ro.build.date.utc",
1235 OPTIONS.target_info_dict),
1236 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001237
Doug Zongker05d3dea2009-06-22 11:32:31 -07001238 device_specific = common.DeviceSpecificParams(
1239 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001240 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001241 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001242 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001243 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001244 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001245 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001246 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001247
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001248 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001249 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001250 if HasVendorPartition(target_zip):
1251 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001252 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001253 else:
1254 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001255
Dan Albert8b72aef2015-03-23 19:13:21 -07001256 target_fp = CalculateFingerprint(oem_props, oem_dict,
1257 OPTIONS.target_info_dict)
1258 source_fp = CalculateFingerprint(oem_props, oem_dict,
1259 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001260
1261 if oem_props is None:
1262 script.AssertSomeFingerprint(source_fp, target_fp)
1263 else:
1264 script.AssertSomeThumbprint(
1265 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1266 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1267
Doug Zongker2ea21062010-04-28 16:05:21 -07001268 metadata["pre-build"] = source_fp
1269 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001270
Doug Zongker55d93282011-01-25 17:03:34 -08001271 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001272 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1273 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001274 target_boot = common.GetBootableImage(
1275 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001276 updating_boot = (not OPTIONS.two_step and
1277 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001278
Doug Zongker55d93282011-01-25 17:03:34 -08001279 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001280 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1281 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001282 target_recovery = common.GetBootableImage(
1283 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001284 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001285
Doug Zongker881dd402009-09-20 14:03:55 -07001286 # Here's how we divide up the progress bar:
1287 # 0.1 for verifying the start state (PatchCheck calls)
1288 # 0.8 for applying patches (ApplyPatch calls)
1289 # 0.1 for unpacking verbatim files, symlinking, and doing the
1290 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001291
Michael Runge6e836112014-04-15 17:40:21 -07001292 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001293 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001294
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001295 # Two-step incremental package strategy (in chronological order,
1296 # which is *not* the order in which the generated script has
1297 # things):
1298 #
1299 # if stage is not "2/3" or "3/3":
1300 # do verification on current system
1301 # write recovery image to boot partition
1302 # set stage to "2/3"
1303 # reboot to boot partition and restart recovery
1304 # else if stage is "2/3":
1305 # write recovery image to recovery partition
1306 # set stage to "3/3"
1307 # reboot to recovery partition and restart recovery
1308 # else:
1309 # (stage must be "3/3")
1310 # perform update:
1311 # patch system files, etc.
1312 # force full install of new boot image
1313 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001314 # complete script normally
1315 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001316
1317 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001318 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001319 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001320 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001321 assert fs.fs_type.upper() == "EMMC", \
1322 "two-step packages only supported on devices with EMMC /misc partitions"
1323 bcb_dev = {"bcb_dev": fs.device}
1324 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1325 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001326if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001327""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001328 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001329 script.WriteRawImage("/recovery", "recovery.img")
1330 script.AppendExtra("""
1331set_stage("%(bcb_dev)s", "3/3");
1332reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001333else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001334""" % bcb_dev)
1335
Tao Bao6c55a8a2015-04-08 15:30:27 -07001336 # Dump fingerprints
1337 script.Print("Source: %s" % (source_fp,))
1338 script.Print("Target: %s" % (target_fp,))
1339
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001340 script.Print("Verifying current system...")
1341
Doug Zongkere5ff5902012-01-17 10:55:37 -08001342 device_specific.IncrementalOTA_VerifyBegin()
1343
Doug Zongker881dd402009-09-20 14:03:55 -07001344 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001345 so_far = system_diff.EmitVerification(script)
1346 if vendor_diff:
1347 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001348
Doug Zongker5da317e2009-06-02 13:38:17 -07001349 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001350 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001351 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001352 print "boot target: %d source: %d diff: %d" % (
1353 target_boot.size, source_boot.size, len(d))
1354
Doug Zongker048e7ca2009-06-15 14:31:53 -07001355 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001356
Tao Baodd24da92015-07-29 14:09:23 -07001357 boot_type, boot_device = common.GetTypeAndDevice(
1358 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001359
1360 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1361 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001362 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001363 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001364 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001365
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001366 size = []
Dan Albert8b72aef2015-03-23 19:13:21 -07001367 if system_diff.patch_list:
1368 size.append(system_diff.largest_source_size)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001369 if vendor_diff:
Dan Albert8b72aef2015-03-23 19:13:21 -07001370 if vendor_diff.patch_list:
1371 size.append(vendor_diff.largest_source_size)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001372 if size or updating_recovery or updating_boot:
1373 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001374
Doug Zongker05d3dea2009-06-22 11:32:31 -07001375 device_specific.IncrementalOTA_VerifyEnd()
1376
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001377 if OPTIONS.two_step:
1378 script.WriteRawImage("/boot", "recovery.img")
1379 script.AppendExtra("""
1380set_stage("%(bcb_dev)s", "2/3");
1381reboot_now("%(bcb_dev)s", "");
1382else
1383""" % bcb_dev)
1384
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001385 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001386
Doug Zongkere5ff5902012-01-17 10:55:37 -08001387 device_specific.IncrementalOTA_InstallBegin()
1388
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001389 if OPTIONS.two_step:
1390 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1391 script.WriteRawImage("/boot", "boot.img")
1392 print "writing full boot image (forced by two-step mode)"
1393
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001394 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001395 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1396 if vendor_diff:
1397 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001398
Doug Zongker881dd402009-09-20 14:03:55 -07001399 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001400 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1401 if vendor_diff:
1402 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001403 if updating_boot:
1404 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001405
1406 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001407 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1408 if vendor_diff:
1409 script.Print("Patching vendor files...")
1410 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001411
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001412 if not OPTIONS.two_step:
1413 if updating_boot:
1414 # Produce the boot image by applying a patch to the current
1415 # contents of the boot partition, and write it back to the
1416 # partition.
1417 script.Print("Patching boot image...")
1418 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1419 % (boot_type, boot_device,
1420 source_boot.size, source_boot.sha1,
1421 target_boot.size, target_boot.sha1),
1422 "-",
1423 target_boot.size, target_boot.sha1,
1424 source_boot.sha1, "patch/boot.img.p")
1425 so_far += target_boot.size
1426 script.SetProgress(so_far / total_patch_size)
1427 print "boot image changed; including."
1428 else:
1429 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001430
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001431 system_items = ItemSet("system", "META/filesystem_config.txt")
1432 if vendor_diff:
1433 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1434
Doug Zongkereef39442009-04-02 12:14:19 -07001435 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001436 # Recovery is generated as a patch using both the boot image
1437 # (which contains the same linux kernel as recovery) and the file
1438 # /system/etc/recovery-resource.dat (which contains all the images
1439 # used in the recovery UI) as sources. This lets us minimize the
1440 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001441 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001442 # For older builds where recovery-resource.dat is not present, we
1443 # use only the boot image as the source.
1444
Doug Zongkerc9253822014-02-04 12:17:58 -08001445 if not target_has_recovery_patch:
1446 def output_sink(fn, data):
1447 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001448 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001449
1450 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1451 target_recovery, target_boot)
1452 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001453 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001454 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001455 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001456 else:
1457 print "recovery image unchanged; skipping."
1458
Doug Zongker881dd402009-09-20 14:03:55 -07001459 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001460
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001461 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1462 if vendor_diff:
1463 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1464
1465 temp_script = script.MakeTemporary()
1466 system_items.GetMetadata(target_zip)
1467 system_items.Get("system").SetPermissions(temp_script)
1468 if vendor_diff:
1469 vendor_items.GetMetadata(target_zip)
1470 vendor_items.Get("vendor").SetPermissions(temp_script)
1471
1472 # Note that this call will mess up the trees of Items, so make sure
1473 # we're done with them.
1474 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1475 if vendor_diff:
1476 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001477
1478 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001479 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1480
1481 # Delete all the symlinks in source that aren't in target. This
1482 # needs to happen before verbatim files are unpacked, in case a
1483 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001484
1485 # If a symlink in the source will be replaced by a regular file, we cannot
1486 # delete the symlink/file in case the package gets applied again. For such
1487 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1488 # (Bug: 23646151)
1489 replaced_symlinks = dict()
1490 if system_diff:
1491 for i in system_diff.verbatim_targets:
1492 replaced_symlinks["/%s" % (i[0],)] = i[2]
1493 if vendor_diff:
1494 for i in vendor_diff.verbatim_targets:
1495 replaced_symlinks["/%s" % (i[0],)] = i[2]
1496
1497 if system_diff:
1498 for tf in system_diff.renames.values():
1499 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1500 if vendor_diff:
1501 for tf in vendor_diff.renames.values():
1502 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1503
1504 always_delete = []
1505 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001506 for dest, link in source_symlinks:
1507 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001508 if link in replaced_symlinks:
1509 may_delete.append((link, replaced_symlinks[link]))
1510 else:
1511 always_delete.append(link)
1512 script.DeleteFiles(always_delete)
1513 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001514
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001515 if system_diff.verbatim_targets:
1516 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001517 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001518 if vendor_diff and vendor_diff.verbatim_targets:
1519 script.Print("Unpacking new vendor files...")
1520 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001521
Doug Zongkerc9253822014-02-04 12:17:58 -08001522 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001523 script.Print("Unpacking new recovery...")
1524 script.UnpackPackageDir("recovery", "/system")
1525
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001526 system_diff.EmitRenames(script)
1527 if vendor_diff:
1528 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001529
Doug Zongker05d3dea2009-06-22 11:32:31 -07001530 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001531
1532 # Create all the symlinks that don't already exist, or point to
1533 # somewhere different than what we want. Delete each symlink before
1534 # creating it, since the 'symlink' command won't overwrite.
1535 to_create = []
1536 for dest, link in target_symlinks:
1537 if link in source_symlinks_d:
1538 if dest != source_symlinks_d[link]:
1539 to_create.append((dest, link))
1540 else:
1541 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001542 script.DeleteFiles([i[1] for i in to_create])
1543 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001544
1545 # Now that the symlinks are created, we can set all the
1546 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001547 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001548
Doug Zongker881dd402009-09-20 14:03:55 -07001549 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001550 device_specific.IncrementalOTA_InstallEnd()
1551
Doug Zongker1c390a22009-05-14 19:06:36 -07001552 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001553 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001554
Doug Zongkere92f15a2011-08-26 13:46:40 -07001555 # Patch the build.prop file last, so if something fails but the
1556 # device can still come up, it appears to be the old build and will
1557 # get set the OTA package again to retry.
1558 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001559 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001560
Doug Zongker922206e2014-03-04 13:16:24 -08001561 if OPTIONS.wipe_user_data:
1562 script.Print("Erasing user data...")
1563 script.FormatPartition("/data")
1564
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001565 if OPTIONS.two_step:
1566 script.AppendExtra("""
1567set_stage("%(bcb_dev)s", "");
1568endif;
1569endif;
1570""" % bcb_dev)
1571
Michael Runge63f01de2014-10-28 19:24:19 -07001572 if OPTIONS.verify and system_diff:
1573 script.Print("Remounting and verifying system partition files...")
1574 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001575 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001576 system_diff.EmitExplicitTargetVerification(script)
1577
1578 if OPTIONS.verify and vendor_diff:
1579 script.Print("Remounting and verifying vendor partition files...")
1580 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001581 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001582 vendor_diff.EmitExplicitTargetVerification(script)
Doug Zongker25568482014-03-03 10:21:27 -08001583 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001584
Doug Zongker2ea21062010-04-28 16:05:21 -07001585 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001586
1587
1588def main(argv):
1589
1590 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001591 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001592 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001593 elif o in ("-k", "--package_key"):
1594 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001595 elif o in ("-i", "--incremental_from"):
1596 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001597 elif o == "--full_radio":
1598 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001599 elif o == "--full_bootloader":
1600 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001601 elif o in ("-w", "--wipe_user_data"):
1602 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001603 elif o in ("-n", "--no_prereq"):
1604 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001605 elif o in ("-o", "--oem_settings"):
1606 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001607 elif o in ("-e", "--extra_script"):
1608 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001609 elif o in ("-a", "--aslr_mode"):
1610 if a in ("on", "On", "true", "True", "yes", "Yes"):
1611 OPTIONS.aslr_mode = True
1612 else:
1613 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001614 elif o in ("-t", "--worker_threads"):
1615 if a.isdigit():
1616 OPTIONS.worker_threads = int(a)
1617 else:
1618 raise ValueError("Cannot parse value %r for option %r - only "
1619 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001620 elif o in ("-2", "--two_step"):
1621 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001622 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001623 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001624 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001625 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001626 elif o == "--block":
1627 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001628 elif o in ("-b", "--binary"):
1629 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001630 elif o in ("--no_fallback_to_full",):
1631 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001632 elif o == "--stash_threshold":
1633 try:
1634 OPTIONS.stash_threshold = float(a)
1635 except ValueError:
1636 raise ValueError("Cannot parse value %r for option %r - expecting "
1637 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001638 elif o == "--gen_verify":
1639 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001640 elif o == "--log_diff":
1641 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001642 else:
1643 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001644 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001645
1646 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001647 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001648 extra_long_opts=[
1649 "board_config=",
1650 "package_key=",
1651 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001652 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001653 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001654 "wipe_user_data",
1655 "no_prereq",
1656 "extra_script=",
1657 "worker_threads=",
1658 "aslr_mode=",
1659 "two_step",
1660 "no_signing",
1661 "block",
1662 "binary=",
1663 "oem_settings=",
1664 "verify",
1665 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001666 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001667 "gen_verify",
1668 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001669 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001670
1671 if len(args) != 2:
1672 common.Usage(__doc__)
1673 sys.exit(1)
1674
Doug Zongker1c390a22009-05-14 19:06:36 -07001675 if OPTIONS.extra_script is not None:
1676 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1677
Doug Zongkereef39442009-04-02 12:14:19 -07001678 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001679 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001680
Doug Zongkereef39442009-04-02 12:14:19 -07001681 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001682 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001683
Doug Zongker37974732010-09-16 17:44:38 -07001684 if OPTIONS.verbose:
1685 print "--- target info ---"
1686 common.DumpInfoDict(OPTIONS.info_dict)
1687
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001688 # If the caller explicitly specified the device-specific extensions
1689 # path via -s/--device_specific, use that. Otherwise, use
1690 # META/releasetools.py if it is present in the target target_files.
1691 # Otherwise, take the path of the file from 'tool_extensions' in the
1692 # info dict and look for that in the local filesystem, relative to
1693 # the current directory.
1694
Doug Zongker37974732010-09-16 17:44:38 -07001695 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001696 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1697 if os.path.exists(from_input):
1698 print "(using device-specific extensions from target_files)"
1699 OPTIONS.device_specific = from_input
1700 else:
1701 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1702
Doug Zongker37974732010-09-16 17:44:38 -07001703 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001704 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001705
Tao Baodb45efa2015-10-27 19:25:18 -07001706 if OPTIONS.info_dict.get("no_recovery") == "true":
1707 raise common.ExternalError(
1708 "--- target build has specified no recovery ---")
1709
Tao Bao767e3ac2015-11-10 12:19:19 -08001710 # Use the default key to sign the package if not specified with package_key.
1711 if not OPTIONS.no_signing:
1712 if OPTIONS.package_key is None:
1713 OPTIONS.package_key = OPTIONS.info_dict.get(
1714 "default_system_dev_certificate",
1715 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001716
Tao Bao767e3ac2015-11-10 12:19:19 -08001717 # Set up the output zip. Create a temporary zip file if signing is needed.
1718 if OPTIONS.no_signing:
1719 if os.path.exists(args[1]):
1720 os.unlink(args[1])
1721 output_zip = zipfile.ZipFile(args[1], "w",
1722 compression=zipfile.ZIP_DEFLATED)
1723 else:
1724 temp_zip_file = tempfile.NamedTemporaryFile()
1725 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1726 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001727
Tao Bao767e3ac2015-11-10 12:19:19 -08001728 cache_size = OPTIONS.info_dict.get("cache_size", None)
1729 if cache_size is None:
1730 print "--- can't determine the cache partition size ---"
1731 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001732
Tao Bao9bc6bb22015-11-09 16:58:28 -08001733 # Generate a verify package.
1734 if OPTIONS.gen_verify:
1735 WriteVerifyPackage(input_zip, output_zip)
1736
Tao Bao767e3ac2015-11-10 12:19:19 -08001737 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001738 elif OPTIONS.incremental_source is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08001739 WriteFullOTAPackage(input_zip, output_zip)
1740
1741 # Generate an incremental OTA. It will fall back to generate a full OTA on
1742 # failure unless no_fallback_to_full is specified.
1743 else:
1744 print "unzipping source target-files..."
1745 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
1746 OPTIONS.incremental_source)
1747 OPTIONS.target_info_dict = OPTIONS.info_dict
1748 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1749 OPTIONS.source_tmp)
1750 if OPTIONS.verbose:
1751 print "--- source info ---"
1752 common.DumpInfoDict(OPTIONS.source_info_dict)
1753 try:
1754 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001755 if OPTIONS.log_diff:
1756 out_file = open(OPTIONS.log_diff, 'w')
1757 import target_files_diff
1758 target_files_diff.recursiveDiff('',
1759 OPTIONS.source_tmp,
1760 OPTIONS.input_tmp,
1761 out_file)
1762 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001763 except ValueError:
1764 if not OPTIONS.fallback_to_full:
1765 raise
1766 print "--- failed to build incremental; falling back to full ---"
1767 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001768 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001769
Tao Bao767e3ac2015-11-10 12:19:19 -08001770 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001771
Tao Bao767e3ac2015-11-10 12:19:19 -08001772 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001773 if not OPTIONS.no_signing:
1774 SignOutput(temp_zip_file.name, args[1])
1775 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001776
Doug Zongkereef39442009-04-02 12:14:19 -07001777 print "done."
1778
1779
1780if __name__ == '__main__':
1781 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001782 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001783 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001784 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07001785 print
1786 print " ERROR: %s" % (e,)
1787 print
1788 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001789 finally:
1790 common.Cleanup()