blob: ed300a7c747fd792d3683231366144f03135de8f [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
Tao Baoc098e9e2016-01-07 13:03:56 -0800112import subprocess
Doug Zongkereef39442009-04-02 12:14:19 -0700113import tempfile
Doug Zongkereef39442009-04-02 12:14:19 -0700114import zipfile
115
116import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700117import edify_generator
Doug Zongkerfc44a512014-08-26 13:10:25 -0700118import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700119
120OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700121OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700122OPTIONS.incremental_source = None
Michael Runge63f01de2014-10-28 19:24:19 -0700123OPTIONS.verify = False
Doug Zongkereef39442009-04-02 12:14:19 -0700124OPTIONS.require_verbatim = set()
125OPTIONS.prohibit_verbatim = set(("system/build.prop",))
126OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700127OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700128OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700129OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700130OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700131OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
132if OPTIONS.worker_threads == 0:
133 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800134OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900135OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800136OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800137OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700138OPTIONS.oem_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -0700139OPTIONS.fallback_to_full = True
Tao Bao43078aa2015-04-21 14:32:35 -0700140OPTIONS.full_radio = False
leozwangaa6c1a12015-08-14 10:57:58 -0700141OPTIONS.full_bootloader = False
Tao Baod47d8e12015-05-21 14:09:49 -0700142# Stash size cannot exceed cache_size * threshold.
143OPTIONS.cache_size = None
144OPTIONS.stash_threshold = 0.8
Tao Bao9bc6bb22015-11-09 16:58:28 -0800145OPTIONS.gen_verify = False
Tao Baod62c6032015-11-30 09:40:20 -0800146OPTIONS.log_diff = None
Tao Bao8dcf7382015-05-21 14:09:49 -0700147
Doug Zongkereef39442009-04-02 12:14:19 -0700148def MostPopularKey(d, default):
149 """Given a dict, return the key corresponding to the largest
150 value. Returns 'default' if the dict is empty."""
151 x = [(v, k) for (k, v) in d.iteritems()]
Dan Albert8b72aef2015-03-23 19:13:21 -0700152 if not x:
153 return default
Doug Zongkereef39442009-04-02 12:14:19 -0700154 x.sort()
155 return x[-1][1]
156
157
158def IsSymlink(info):
159 """Return true if the zipfile.ZipInfo object passed in represents a
160 symlink."""
Ying Wang2ffb3142015-07-06 14:02:01 -0700161 return (info.external_attr >> 16) & 0o770000 == 0o120000
Doug Zongkereef39442009-04-02 12:14:19 -0700162
Hristo Bojinov96be7202010-08-02 10:26:17 -0700163def IsRegular(info):
164 """Return true if the zipfile.ZipInfo object passed in represents a
Ying Wang2ffb3142015-07-06 14:02:01 -0700165 regular file."""
166 return (info.external_attr >> 16) & 0o770000 == 0o100000
Doug Zongkereef39442009-04-02 12:14:19 -0700167
Michael Runge4038aa82013-12-13 18:06:28 -0800168def ClosestFileMatch(src, tgtfiles, existing):
169 """Returns the closest file match between a source file and list
170 of potential matches. The exact filename match is preferred,
171 then the sha1 is searched for, and finally a file with the same
172 basename is evaluated. Rename support in the updater-binary is
173 required for the latter checks to be used."""
174
175 result = tgtfiles.get("path:" + src.name)
176 if result is not None:
177 return result
178
179 if not OPTIONS.target_info_dict.get("update_rename_support", False):
180 return None
181
182 if src.size < 1000:
183 return None
184
185 result = tgtfiles.get("sha1:" + src.sha1)
186 if result is not None and existing.get(result.name) is None:
187 return result
188 result = tgtfiles.get("file:" + src.name.split("/")[-1])
189 if result is not None and existing.get(result.name) is None:
190 return result
191 return None
192
Dan Albert8b72aef2015-03-23 19:13:21 -0700193class ItemSet(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700194 def __init__(self, partition, fs_config):
195 self.partition = partition
196 self.fs_config = fs_config
197 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700198
Dan Albert8b72aef2015-03-23 19:13:21 -0700199 def Get(self, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700200 if name not in self.ITEMS:
Dan Albert8b72aef2015-03-23 19:13:21 -0700201 self.ITEMS[name] = Item(self, name, is_dir=is_dir)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700202 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700203
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700204 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700205 # The target_files contains a record of what the uid,
206 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700207 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700208
209 for line in output.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700210 if not line:
211 continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700212 columns = line.split()
213 name, uid, gid, mode = columns[:4]
214 selabel = None
215 capabilities = None
216
217 # After the first 4 columns, there are a series of key=value
218 # pairs. Extract out the fields we care about.
219 for element in columns[4:]:
220 key, value = element.split("=")
221 if key == "selabel":
222 selabel = value
223 if key == "capabilities":
224 capabilities = value
225
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700226 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700227 if i is not None:
228 i.uid = int(uid)
229 i.gid = int(gid)
230 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700231 i.selabel = selabel
232 i.capabilities = capabilities
Dan Albert8b72aef2015-03-23 19:13:21 -0700233 if i.is_dir:
Doug Zongker283e2a12010-03-15 17:52:32 -0700234 i.children.sort(key=lambda i: i.name)
235
Tao Baof2cffbd2015-07-22 12:33:18 -0700236 # Set metadata for the files generated by this script. For full recovery
237 # image at system/etc/recovery.img, it will be taken care by fs_config.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700238 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700239 if i:
240 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700241 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700242 if i:
243 i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700244
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700245
Dan Albert8b72aef2015-03-23 19:13:21 -0700246class Item(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700247 """Items represent the metadata (user, group, mode) of files and
248 directories in the system image."""
Dan Albert8b72aef2015-03-23 19:13:21 -0700249 def __init__(self, itemset, name, is_dir=False):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700250 self.itemset = itemset
251 self.name = name
252 self.uid = None
253 self.gid = None
254 self.mode = None
255 self.selabel = None
256 self.capabilities = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700257 self.is_dir = is_dir
258 self.descendants = None
259 self.best_subtree = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700260
261 if name:
Dan Albert8b72aef2015-03-23 19:13:21 -0700262 self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700263 self.parent.children.append(self)
264 else:
265 self.parent = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700266 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700267 self.children = []
268
269 def Dump(self, indent=0):
270 if self.uid is not None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700271 print "%s%s %d %d %o" % (
272 " " * indent, self.name, self.uid, self.gid, self.mode)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700273 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700274 print "%s%s %s %s %s" % (
275 " " * indent, self.name, self.uid, self.gid, self.mode)
276 if self.is_dir:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700277 print "%s%s" % (" "*indent, self.descendants)
278 print "%s%s" % (" "*indent, self.best_subtree)
279 for i in self.children:
280 i.Dump(indent=indent+1)
281
Doug Zongkereef39442009-04-02 12:14:19 -0700282 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700283 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
Dan Albert8b72aef2015-03-23 19:13:21 -0700284 all children and determine the best strategy for using set_perm_recursive
285 and set_perm to correctly chown/chmod all the files to their desired
Doug Zongkereef39442009-04-02 12:14:19 -0700286 values. Recursively calls itself for all descendants.
287
Dan Albert8b72aef2015-03-23 19:13:21 -0700288 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
289 counting up all descendants of this node. (dmode or fmode may be None.)
290 Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
291 fmode, selabel, capabilities) tuple that will match the most descendants of
292 that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700293 """
294
Dan Albert8b72aef2015-03-23 19:13:21 -0700295 assert self.is_dir
296 key = (self.uid, self.gid, self.mode, None, self.selabel,
297 self.capabilities)
298 self.descendants = {key: 1}
299 d = self.descendants
Doug Zongkereef39442009-04-02 12:14:19 -0700300 for i in self.children:
Dan Albert8b72aef2015-03-23 19:13:21 -0700301 if i.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700302 for k, v in i.CountChildMetadata().iteritems():
303 d[k] = d.get(k, 0) + v
304 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700305 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700306 d[k] = d.get(k, 0) + 1
307
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700308 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
309 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700310
311 # First, find the (uid, gid) pair that matches the most
312 # descendants.
313 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700314 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700315 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
316 ug = MostPopularKey(ug, (0, 0))
317
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700318 # Now find the dmode, fmode, selabel, and capabilities that match
319 # the most descendants with that (uid, gid), and choose those.
Dan Albert8b72aef2015-03-23 19:13:21 -0700320 best_dmode = (0, 0o755)
321 best_fmode = (0, 0o644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700322 best_selabel = (0, None)
323 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700324 for k, count in d.iteritems():
Dan Albert8b72aef2015-03-23 19:13:21 -0700325 if k[:2] != ug:
326 continue
327 if k[2] is not None and count >= best_dmode[0]:
328 best_dmode = (count, k[2])
329 if k[3] is not None and count >= best_fmode[0]:
330 best_fmode = (count, k[3])
331 if k[4] is not None and count >= best_selabel[0]:
332 best_selabel = (count, k[4])
333 if k[5] is not None and count >= best_capabilities[0]:
334 best_capabilities = (count, k[5])
335 self.best_subtree = ug + (
336 best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700337
338 return d
339
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700340 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700341 """Append set_perm/set_perm_recursive commands to 'script' to
342 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700343 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700344
345 self.CountChildMetadata()
346
347 def recurse(item, current):
Dan Albert8b72aef2015-03-23 19:13:21 -0700348 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
349 # that the current item (and all its children) have already been set to.
350 # We only need to issue set_perm/set_perm_recursive commands if we're
Doug Zongkereef39442009-04-02 12:14:19 -0700351 # supposed to be something different.
Dan Albert8b72aef2015-03-23 19:13:21 -0700352 if item.is_dir:
Doug Zongkereef39442009-04-02 12:14:19 -0700353 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700354 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700355 current = item.best_subtree
356
357 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700358 item.mode != current[2] or item.selabel != current[4] or \
359 item.capabilities != current[5]:
360 script.SetPermissions("/"+item.name, item.uid, item.gid,
361 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700362
363 for i in item.children:
364 recurse(i, current)
365 else:
366 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700367 item.mode != current[3] or item.selabel != current[4] or \
368 item.capabilities != current[5]:
369 script.SetPermissions("/"+item.name, item.uid, item.gid,
370 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700371
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700372 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700373
374
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700375def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
376 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700377 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800378 list of symlinks. output_zip may be None, in which case the copy is
379 skipped (but the other side effects still happen). substitute is an
380 optional dict of {output filename: contents} to be output instead of
381 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700382 """
383
384 symlinks = []
385
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700386 partition = itemset.partition
387
Doug Zongkereef39442009-04-02 12:14:19 -0700388 for info in input_zip.infolist():
Tao Baoeaf885b2015-03-23 16:01:17 -0700389 prefix = partition.upper() + "/"
390 if info.filename.startswith(prefix):
391 basefilename = info.filename[len(prefix):]
Doug Zongkereef39442009-04-02 12:14:19 -0700392 if IsSymlink(info):
393 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700394 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700395 else:
Tao Bao2ed665a2015-04-01 11:21:55 -0700396 import copy
Doug Zongkereef39442009-04-02 12:14:19 -0700397 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700398 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700399 if substitute and fn in substitute and substitute[fn] is None:
400 continue
401 if output_zip is not None:
402 if substitute and fn in substitute:
403 data = substitute[fn]
404 else:
405 data = input_zip.read(info.filename)
Tao Bao2ed665a2015-04-01 11:21:55 -0700406 common.ZipWriteStr(output_zip, info2, data)
Doug Zongkereef39442009-04-02 12:14:19 -0700407 if fn.endswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700408 itemset.Get(fn[:-1], is_dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700409 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700410 itemset.Get(fn)
Doug Zongkereef39442009-04-02 12:14:19 -0700411
412 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800413 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700414
415
Doug Zongkereef39442009-04-02 12:14:19 -0700416def SignOutput(temp_zip_name, output_zip_name):
417 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
418 pw = key_passwords[OPTIONS.package_key]
419
Doug Zongker951495f2009-08-14 12:44:19 -0700420 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
421 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700422
423
Dan Albert8b72aef2015-03-23 19:13:21 -0700424def AppendAssertions(script, info_dict, oem_dict=None):
Michael Runge6e836112014-04-15 17:40:21 -0700425 oem_props = info_dict.get("oem_fingerprint_properties")
Michael Runge560569a2014-09-18 15:12:45 -0700426 if oem_props is None or len(oem_props) == 0:
Michael Runge6e836112014-04-15 17:40:21 -0700427 device = GetBuildProp("ro.product.device", info_dict)
428 script.AssertDevice(device)
429 else:
430 if oem_dict is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700431 raise common.ExternalError(
432 "No OEM file provided to answer expected assertions")
Michael Runge6e836112014-04-15 17:40:21 -0700433 for prop in oem_props.split():
434 if oem_dict.get(prop) is None:
Dan Albert8b72aef2015-03-23 19:13:21 -0700435 raise common.ExternalError(
436 "The OEM file is missing the property %s" % prop)
Michael Runge6e836112014-04-15 17:40:21 -0700437 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700438
Doug Zongkereef39442009-04-02 12:14:19 -0700439
Doug Zongkerc9253822014-02-04 12:17:58 -0800440def HasRecoveryPatch(target_files_zip):
Tao Baof2cffbd2015-07-22 12:33:18 -0700441 namelist = [name for name in target_files_zip.namelist()]
442 return ("SYSTEM/recovery-from-boot.p" in namelist or
443 "SYSTEM/etc/recovery.img" in namelist)
Doug Zongker73ef8252009-07-23 15:12:53 -0700444
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700445def HasVendorPartition(target_files_zip):
446 try:
447 target_files_zip.getinfo("VENDOR/")
448 return True
449 except KeyError:
450 return False
451
Michael Runge6e836112014-04-15 17:40:21 -0700452def GetOemProperty(name, oem_props, oem_dict, info_dict):
453 if oem_props is not None and name in oem_props:
454 return oem_dict[name]
455 return GetBuildProp(name, info_dict)
456
457
458def CalculateFingerprint(oem_props, oem_dict, info_dict):
459 if oem_props is None:
460 return GetBuildProp("ro.build.fingerprint", info_dict)
461 return "%s/%s/%s:%s" % (
Dan Albert8b72aef2015-03-23 19:13:21 -0700462 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
463 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
464 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
465 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700466
Doug Zongkerfc44a512014-08-26 13:10:25 -0700467
Doug Zongker3c84f562014-07-31 11:06:30 -0700468def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700469 # Return an image object (suitable for passing to BlockImageDiff)
470 # for the 'which' partition (most be "system" or "vendor"). If a
471 # prebuilt image and file map are found in tmpdir they are used,
472 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700473
474 assert which in ("system", "vendor")
475
476 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700477 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
478 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700479 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700480 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700481
482 else:
483 print "building %s.img from target-files" % (which,)
484
485 # This is an 'old' target-files, which does not contain images
486 # already built. Build them.
487
Doug Zongkerfc44a512014-08-26 13:10:25 -0700488 mappath = tempfile.mkstemp()[1]
489 OPTIONS.tempfiles.append(mappath)
490
Doug Zongker3c84f562014-07-31 11:06:30 -0700491 import add_img_to_target_files
492 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700493 path = add_img_to_target_files.BuildSystem(
494 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700495 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700496 path = add_img_to_target_files.BuildVendor(
497 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700498
Tao Baoff777812015-05-12 11:42:31 -0700499 # Bug: http://b/20939131
500 # In ext4 filesystems, block 0 might be changed even being mounted
501 # R/O. We add it to clobbered_blocks so that it will be written to the
502 # target unconditionally. Note that they are still part of care_map.
503 clobbered_blocks = "0"
504
505 return sparse_img.SparseImage(path, mappath, clobbered_blocks)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700506
507
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700508def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700509 # TODO: how to determine this? We don't know what version it will
Tao Bao34b47bf2015-06-22 19:17:41 -0700510 # be installed on top of. For now, we expect the API just won't
511 # change very often. Similarly for fstab, it might have changed
512 # in the target build.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700513 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700514
Michael Runge6e836112014-04-15 17:40:21 -0700515 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700516 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700517 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700518 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700519 if OPTIONS.oem_source is None:
520 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700521 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -0700522 oem_dict = common.LoadDictionaryFromLines(
523 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -0700524
Dan Albert8b72aef2015-03-23 19:13:21 -0700525 metadata = {
526 "post-build": CalculateFingerprint(oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700527 OPTIONS.info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700528 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
529 OPTIONS.info_dict),
530 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
531 }
Doug Zongker2ea21062010-04-28 16:05:21 -0700532
Doug Zongker05d3dea2009-06-22 11:32:31 -0700533 device_specific = common.DeviceSpecificParams(
534 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700535 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700536 output_zip=output_zip,
537 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700538 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700539 metadata=metadata,
540 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700541
Doug Zongkerc9253822014-02-04 12:17:58 -0800542 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800543 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800544
Doug Zongker962069c2009-04-23 11:41:58 -0700545 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700546 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700547 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
548 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700549
Michael Runge6e836112014-04-15 17:40:21 -0700550 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700551 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800552
553 # Two-step package strategy (in chronological order, which is *not*
554 # the order in which the generated script has things):
555 #
556 # if stage is not "2/3" or "3/3":
557 # write recovery image to boot partition
558 # set stage to "2/3"
559 # reboot to boot partition and restart recovery
560 # else if stage is "2/3":
561 # write recovery image to recovery partition
562 # set stage to "3/3"
563 # reboot to recovery partition and restart recovery
564 # else:
565 # (stage must be "3/3")
566 # set stage to ""
567 # do normal full package installation:
568 # wipe and install system, boot image, etc.
569 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700570 # complete script normally
571 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800572
573 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
574 OPTIONS.input_tmp, "RECOVERY")
575 if OPTIONS.two_step:
576 if not OPTIONS.info_dict.get("multistage_support", None):
577 assert False, "two-step packages not supported by this build"
578 fs = OPTIONS.info_dict["fstab"]["/misc"]
579 assert fs.fs_type.upper() == "EMMC", \
580 "two-step packages only supported on devices with EMMC /misc partitions"
581 bcb_dev = {"bcb_dev": fs.device}
582 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
583 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700584if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800585""" % bcb_dev)
586 script.WriteRawImage("/recovery", "recovery.img")
587 script.AppendExtra("""
588set_stage("%(bcb_dev)s", "3/3");
589reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700590else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800591""" % bcb_dev)
592
Tao Bao6c55a8a2015-04-08 15:30:27 -0700593 # Dump fingerprints
594 script.Print("Target: %s" % CalculateFingerprint(
595 oem_props, oem_dict, OPTIONS.info_dict))
596
Doug Zongkere5ff5902012-01-17 10:55:37 -0800597 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700598
Doug Zongker01ce19c2014-02-04 13:48:15 -0800599 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700600
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700601 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800602 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700603 if HasVendorPartition(input_zip):
604 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700605
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400606 # Place a copy of file_contexts.bin into the OTA package which will be used
607 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700608 if "selinux_fc" in OPTIONS.info_dict:
609 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500610
Michael Runge7cd99ba2014-10-22 17:21:48 -0700611 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
612
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700613 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700614 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800615
Doug Zongker26e66192014-02-20 13:22:07 -0800616 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700617 # Full OTA is done as an "incremental" against an empty source
618 # image. This has the effect of writing new data from the package
619 # to the entire partition, but lets us reuse the updater code that
620 # writes incrementals to do it.
621 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
622 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700623 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700624 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800625 else:
626 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700627 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800628 if not has_recovery_patch:
629 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800630 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700631
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700632 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800633 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700634
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700635 boot_img = common.GetBootableImage(
636 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800637
Doug Zongker91a99c22014-05-09 13:15:01 -0700638 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800639 def output_sink(fn, data):
640 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700641 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800642
643 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
644 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700645
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700646 system_items.GetMetadata(input_zip)
647 system_items.Get("system").SetPermissions(script)
648
649 if HasVendorPartition(input_zip):
650 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
651 script.ShowProgress(0.1, 0)
652
653 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700654 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
655 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700656 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700657 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700658 else:
659 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700660 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700661 script.UnpackPackageDir("vendor", "/vendor")
662
663 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
664 script.MakeSymlinks(symlinks)
665
666 vendor_items.GetMetadata(input_zip)
667 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700668
Doug Zongker37974732010-09-16 17:44:38 -0700669 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700670 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700671
Doug Zongker01ce19c2014-02-04 13:48:15 -0800672 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700673 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700674
Doug Zongker01ce19c2014-02-04 13:48:15 -0800675 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700676 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700677
Doug Zongker1c390a22009-05-14 19:06:36 -0700678 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700679 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700680
Doug Zongker14833602010-02-02 13:12:04 -0800681 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800682
Doug Zongker922206e2014-03-04 13:16:24 -0800683 if OPTIONS.wipe_user_data:
684 script.ShowProgress(0.1, 10)
685 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700686
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800687 if OPTIONS.two_step:
688 script.AppendExtra("""
689set_stage("%(bcb_dev)s", "");
690""" % bcb_dev)
691 script.AppendExtra("else\n")
692 script.WriteRawImage("/boot", "recovery.img")
693 script.AppendExtra("""
694set_stage("%(bcb_dev)s", "2/3");
695reboot_now("%(bcb_dev)s", "");
696endif;
697endif;
698""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800699 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700700 WriteMetadata(metadata, output_zip)
701
Doug Zongkerfc44a512014-08-26 13:10:25 -0700702
Dan Albert8e0178d2015-01-27 15:53:15 -0800703def WritePolicyConfig(file_name, output_zip):
704 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500705
Doug Zongker2ea21062010-04-28 16:05:21 -0700706
707def WriteMetadata(metadata, output_zip):
708 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
709 "".join(["%s=%s\n" % kv
710 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700711
Doug Zongkerfc44a512014-08-26 13:10:25 -0700712
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700713def LoadPartitionFiles(z, partition):
714 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700715 ZipFile, and return a dict of {filename: File object}."""
716 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700717 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700718 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700719 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700720 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700721 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700722 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700723 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800724 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700725
726
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700727def GetBuildProp(prop, info_dict):
728 """Return the fingerprint of the build of a given target-files info_dict."""
729 try:
730 return info_dict.get("build.prop", {})[prop]
731 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700732 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700733
Doug Zongkerfc44a512014-08-26 13:10:25 -0700734
Michael Runge4038aa82013-12-13 18:06:28 -0800735def AddToKnownPaths(filename, known_paths):
736 if filename[-1] == "/":
737 return
738 dirs = filename.split("/")[:-1]
739 while len(dirs) > 0:
740 path = "/".join(dirs)
741 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700742 break
Michael Runge4038aa82013-12-13 18:06:28 -0800743 known_paths.add(path)
744 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700745
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700746
Geremy Condra36bd3652014-02-06 19:45:10 -0800747def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700748 # TODO(tbao): We should factor out the common parts between
749 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800750 source_version = OPTIONS.source_info_dict["recovery_api_version"]
751 target_version = OPTIONS.target_info_dict["recovery_api_version"]
752
753 if source_version == 0:
754 print ("WARNING: generating edify script for a source that "
755 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700756 script = edify_generator.EdifyGenerator(
757 source_version, OPTIONS.target_info_dict,
758 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800759
Tao Bao3806c232015-07-05 21:08:33 -0700760 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
761 recovery_mount_options = OPTIONS.source_info_dict.get(
762 "recovery_mount_options")
763 oem_dict = None
764 if oem_props is not None and len(oem_props) > 0:
765 if OPTIONS.oem_source is None:
766 raise common.ExternalError("OEM source required for this build")
767 script.Mount("/oem", recovery_mount_options)
768 oem_dict = common.LoadDictionaryFromLines(
769 open(OPTIONS.oem_source).readlines())
770
Dan Albert8b72aef2015-03-23 19:13:21 -0700771 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700772 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
773 OPTIONS.source_info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700774 "post-timestamp": GetBuildProp("ro.build.date.utc",
775 OPTIONS.target_info_dict),
776 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800777
778 device_specific = common.DeviceSpecificParams(
779 source_zip=source_zip,
780 source_version=source_version,
781 target_zip=target_zip,
782 target_version=target_version,
783 output_zip=output_zip,
784 script=script,
785 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700786 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800787
Tao Bao3806c232015-07-05 21:08:33 -0700788 source_fp = CalculateFingerprint(oem_props, oem_dict,
789 OPTIONS.source_info_dict)
790 target_fp = CalculateFingerprint(oem_props, oem_dict,
791 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800792 metadata["pre-build"] = source_fp
793 metadata["post-build"] = target_fp
794
795 source_boot = common.GetBootableImage(
796 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
797 OPTIONS.source_info_dict)
798 target_boot = common.GetBootableImage(
799 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
800 updating_boot = (not OPTIONS.two_step and
801 (source_boot.data != target_boot.data))
802
Geremy Condra36bd3652014-02-06 19:45:10 -0800803 target_recovery = common.GetBootableImage(
804 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800805
Doug Zongkerfc44a512014-08-26 13:10:25 -0700806 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
807 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700808
809 blockimgdiff_version = 1
810 if OPTIONS.info_dict:
811 blockimgdiff_version = max(
812 int(i) for i in
813 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
814
Tianjie Xufc3422a2015-12-15 11:53:59 -0800815 # Check first block of system partition for remount R/W only if
816 # disk type is ext4
817 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
818 check_first_block = system_partition.fs_type=="ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700819 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800820 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700821 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700822
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700823 if HasVendorPartition(target_zip):
824 if not HasVendorPartition(source_zip):
825 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700826 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
827 OPTIONS.source_info_dict)
828 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
829 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800830
831 # Check first block of vendor partition for remount R/W only if
832 # disk type is ext4
833 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
834 check_first_block = vendor_partition.fs_type=="ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700835 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800836 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700837 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700838 else:
839 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800840
Michael Rungec6e3afd2014-05-05 11:55:47 -0700841 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800842 device_specific.IncrementalOTA_Assertions()
843
844 # Two-step incremental package strategy (in chronological order,
845 # which is *not* the order in which the generated script has
846 # things):
847 #
848 # if stage is not "2/3" or "3/3":
849 # do verification on current system
850 # write recovery image to boot partition
851 # set stage to "2/3"
852 # reboot to boot partition and restart recovery
853 # else if stage is "2/3":
854 # write recovery image to recovery partition
855 # set stage to "3/3"
856 # reboot to recovery partition and restart recovery
857 # else:
858 # (stage must be "3/3")
859 # perform update:
860 # patch system files, etc.
861 # force full install of new boot image
862 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700863 # complete script normally
864 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800865
866 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700867 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800868 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700869 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800870 assert fs.fs_type.upper() == "EMMC", \
871 "two-step packages only supported on devices with EMMC /misc partitions"
872 bcb_dev = {"bcb_dev": fs.device}
873 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
874 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700875if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800876""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700877 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800878 script.WriteRawImage("/recovery", "recovery.img")
879 script.AppendExtra("""
880set_stage("%(bcb_dev)s", "3/3");
881reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700882else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800883""" % bcb_dev)
884
Tao Bao6c55a8a2015-04-08 15:30:27 -0700885 # Dump fingerprints
886 script.Print("Source: %s" % CalculateFingerprint(
887 oem_props, oem_dict, OPTIONS.source_info_dict))
888 script.Print("Target: %s" % CalculateFingerprint(
889 oem_props, oem_dict, OPTIONS.target_info_dict))
890
Geremy Condra36bd3652014-02-06 19:45:10 -0800891 script.Print("Verifying current system...")
892
893 device_specific.IncrementalOTA_VerifyBegin()
894
Michael Rungec6e3afd2014-05-05 11:55:47 -0700895 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700896 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
897 # patching on a device that's already on the target build will damage the
898 # system. Because operations like move don't check the block state, they
899 # always apply the changes unconditionally.
900 if blockimgdiff_version <= 2:
901 script.AssertSomeFingerprint(source_fp)
902 else:
903 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700904 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700905 if blockimgdiff_version <= 2:
906 script.AssertSomeThumbprint(
907 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
908 else:
909 script.AssertSomeThumbprint(
910 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
911 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800912
913 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700914 boot_type, boot_device = common.GetTypeAndDevice(
915 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800916 d = common.Difference(target_boot, source_boot)
917 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700918 if d is None:
919 include_full_boot = True
920 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
921 else:
922 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800923
Doug Zongkerf8340082014-08-05 10:39:37 -0700924 print "boot target: %d source: %d diff: %d" % (
925 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800926
Doug Zongkerf8340082014-08-05 10:39:37 -0700927 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800928
Doug Zongkerf8340082014-08-05 10:39:37 -0700929 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
930 (boot_type, boot_device,
931 source_boot.size, source_boot.sha1,
932 target_boot.size, target_boot.sha1))
Geremy Condra36bd3652014-02-06 19:45:10 -0800933
934 device_specific.IncrementalOTA_VerifyEnd()
935
936 if OPTIONS.two_step:
937 script.WriteRawImage("/boot", "recovery.img")
938 script.AppendExtra("""
939set_stage("%(bcb_dev)s", "2/3");
940reboot_now("%(bcb_dev)s", "");
941else
942""" % bcb_dev)
943
Jesse Zhao75bcea02015-01-06 10:59:53 -0800944 # Verify the existing partitions.
945 system_diff.WriteVerifyScript(script)
946 if vendor_diff:
947 vendor_diff.WriteVerifyScript(script)
948
Geremy Condra36bd3652014-02-06 19:45:10 -0800949 script.Comment("---- start making changes here ----")
950
951 device_specific.IncrementalOTA_InstallBegin()
952
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700953 system_diff.WriteScript(script, output_zip,
954 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700955
Doug Zongkerfc44a512014-08-26 13:10:25 -0700956 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700957 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800958
959 if OPTIONS.two_step:
960 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
961 script.WriteRawImage("/boot", "boot.img")
962 print "writing full boot image (forced by two-step mode)"
963
964 if not OPTIONS.two_step:
965 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700966 if include_full_boot:
967 print "boot image changed; including full."
968 script.Print("Installing boot image...")
969 script.WriteRawImage("/boot", "boot.img")
970 else:
971 # Produce the boot image by applying a patch to the current
972 # contents of the boot partition, and write it back to the
973 # partition.
974 print "boot image changed; including patch."
975 script.Print("Patching boot image...")
976 script.ShowProgress(0.1, 10)
977 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
978 % (boot_type, boot_device,
979 source_boot.size, source_boot.sha1,
980 target_boot.size, target_boot.sha1),
981 "-",
982 target_boot.size, target_boot.sha1,
983 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800984 else:
985 print "boot image unchanged; skipping."
986
987 # Do device-specific installation (eg, write radio image).
988 device_specific.IncrementalOTA_InstallEnd()
989
990 if OPTIONS.extra_script is not None:
991 script.AppendExtra(OPTIONS.extra_script)
992
Doug Zongker922206e2014-03-04 13:16:24 -0800993 if OPTIONS.wipe_user_data:
994 script.Print("Erasing user data...")
995 script.FormatPartition("/data")
996
Geremy Condra36bd3652014-02-06 19:45:10 -0800997 if OPTIONS.two_step:
998 script.AppendExtra("""
999set_stage("%(bcb_dev)s", "");
1000endif;
1001endif;
1002""" % bcb_dev)
1003
1004 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -08001005 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -08001006 WriteMetadata(metadata, output_zip)
1007
Doug Zongker32b527d2014-03-04 10:03:02 -08001008
Tao Bao9bc6bb22015-11-09 16:58:28 -08001009def WriteVerifyPackage(input_zip, output_zip):
1010 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1011
1012 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1013 recovery_mount_options = OPTIONS.info_dict.get(
1014 "recovery_mount_options")
1015 oem_dict = None
1016 if oem_props is not None and len(oem_props) > 0:
1017 if OPTIONS.oem_source is None:
1018 raise common.ExternalError("OEM source required for this build")
1019 script.Mount("/oem", recovery_mount_options)
1020 oem_dict = common.LoadDictionaryFromLines(
1021 open(OPTIONS.oem_source).readlines())
1022
1023 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1024 metadata = {
1025 "post-build": target_fp,
1026 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1027 OPTIONS.info_dict),
1028 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1029 }
1030
1031 device_specific = common.DeviceSpecificParams(
1032 input_zip=input_zip,
1033 input_version=OPTIONS.info_dict["recovery_api_version"],
1034 output_zip=output_zip,
1035 script=script,
1036 input_tmp=OPTIONS.input_tmp,
1037 metadata=metadata,
1038 info_dict=OPTIONS.info_dict)
1039
1040 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1041
1042 script.Print("Verifying device images against %s..." % target_fp)
1043 script.AppendExtra("")
1044
1045 script.Print("Verifying boot...")
1046 boot_img = common.GetBootableImage(
1047 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1048 boot_type, boot_device = common.GetTypeAndDevice(
1049 "/boot", OPTIONS.info_dict)
1050 script.Verify("%s:%s:%d:%s" % (
1051 boot_type, boot_device, boot_img.size, boot_img.sha1))
1052 script.AppendExtra("")
1053
1054 script.Print("Verifying recovery...")
1055 recovery_img = common.GetBootableImage(
1056 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1057 recovery_type, recovery_device = common.GetTypeAndDevice(
1058 "/recovery", OPTIONS.info_dict)
1059 script.Verify("%s:%s:%d:%s" % (
1060 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1061 script.AppendExtra("")
1062
1063 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1064 system_tgt.ResetFileMap()
1065 system_diff = common.BlockDifference("system", system_tgt, src=None)
1066 system_diff.WriteStrictVerifyScript(script)
1067
1068 if HasVendorPartition(input_zip):
1069 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1070 vendor_tgt.ResetFileMap()
1071 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1072 vendor_diff.WriteStrictVerifyScript(script)
1073
1074 # Device specific partitions, such as radio, bootloader and etc.
1075 device_specific.VerifyOTA_Assertions()
1076
1077 script.SetProgress(1.0)
1078 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
1079 WriteMetadata(metadata, output_zip)
1080
1081
Tao Baoc098e9e2016-01-07 13:03:56 -08001082def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1083 source_file=None):
1084 """Generate an Android OTA package that has A/B update payload."""
1085
1086 # Setup signing keys.
1087 if OPTIONS.package_key is None:
1088 OPTIONS.package_key = OPTIONS.info_dict.get(
1089 "default_system_dev_certificate",
1090 "build/target/product/security/testkey")
1091
1092 # A/B updater expects key in RSA format.
1093 cmd = ["openssl", "pkcs8",
1094 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1095 "-inform", "DER", "-nocrypt"]
1096 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1097 cmd.extend(["-out", rsa_key])
1098 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1099 p1.wait()
1100 assert p1.returncode == 0, "openssl pkcs8 failed"
1101
1102 # Stage the output zip package for signing.
1103 temp_zip_file = tempfile.NamedTemporaryFile()
1104 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1105 compression=zipfile.ZIP_DEFLATED)
1106
1107 # Metadata to comply with Android OTA package format.
1108 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1109 oem_dict = None
1110 if oem_props:
1111 if OPTIONS.oem_source is None:
1112 raise common.ExternalError("OEM source required for this build")
1113 oem_dict = common.LoadDictionaryFromLines(
1114 open(OPTIONS.oem_source).readlines())
1115
1116 metadata = {
1117 "post-build": CalculateFingerprint(oem_props, oem_dict,
1118 OPTIONS.info_dict),
1119 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1120 OPTIONS.info_dict),
1121 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1122 }
1123
1124 if source_file is not None:
1125 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1126 OPTIONS.source_info_dict)
1127
1128 # 1. Generate payload.
1129 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1130 cmd = ["brillo_update_payload", "generate",
1131 "--payload", payload_file,
1132 "--target_image", target_file]
1133 if source_file is not None:
1134 cmd.extend(["--source_image", source_file])
1135 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1136 p1.wait()
1137 assert p1.returncode == 0, "brillo_update_payload generate failed"
1138
1139 # 2. Generate hashes of the payload and metadata files.
1140 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1141 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1142 cmd = ["brillo_update_payload", "hash",
1143 "--unsigned_payload", payload_file,
1144 "--signature_size", "256",
1145 "--metadata_hash_file", metadata_sig_file,
1146 "--payload_hash_file", payload_sig_file]
1147 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1148 p1.wait()
1149 assert p1.returncode == 0, "brillo_update_payload hash failed"
1150
1151 # 3. Sign the hashes and insert them back into the payload file.
1152 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1153 suffix=".bin")
1154 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1155 suffix=".bin")
1156 # 3a. Sign the payload hash.
1157 cmd = ["openssl", "pkeyutl", "-sign",
1158 "-inkey", rsa_key,
1159 "-pkeyopt", "digest:sha256",
1160 "-in", payload_sig_file,
1161 "-out", signed_payload_sig_file]
1162 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1163 p1.wait()
1164 assert p1.returncode == 0, "openssl sign payload failed"
1165
1166 # 3b. Sign the metadata hash.
1167 cmd = ["openssl", "pkeyutl", "-sign",
1168 "-inkey", rsa_key,
1169 "-pkeyopt", "digest:sha256",
1170 "-in", metadata_sig_file,
1171 "-out", signed_metadata_sig_file]
1172 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1173 p1.wait()
1174 assert p1.returncode == 0, "openssl sign metadata failed"
1175
1176 # 3c. Insert the signatures back into the payload file.
1177 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1178 suffix=".bin")
1179 cmd = ["brillo_update_payload", "sign",
1180 "--unsigned_payload", payload_file,
1181 "--payload", signed_payload_file,
1182 "--signature_size", "256",
1183 "--metadata_signature_file", signed_metadata_sig_file,
1184 "--payload_signature_file", signed_payload_sig_file]
1185 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1186 p1.wait()
1187 assert p1.returncode == 0, "brillo_update_payload sign failed"
1188
Alex Deymo19241c12016-02-04 22:29:29 -08001189 # 4. Dump the signed payload properties.
1190 properties_file = common.MakeTempFile(prefix="payload-properties-",
1191 suffix=".txt")
1192 cmd = ["brillo_update_payload", "properties",
1193 "--payload", signed_payload_file,
1194 "--properties_file", properties_file]
1195 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1196 p1.wait()
1197 assert p1.returncode == 0, "brillo_update_payload properties failed"
1198
1199 # Add the signed payload file and properties into the zip.
1200 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001201 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1202 compress_type=zipfile.ZIP_STORED)
1203 WriteMetadata(metadata, output_zip)
1204
1205 # Sign the whole package to comply with the Android OTA package format.
1206 common.ZipClose(output_zip)
1207 SignOutput(temp_zip_file.name, output_file)
1208 temp_zip_file.close()
1209
1210
Dan Albert8b72aef2015-03-23 19:13:21 -07001211class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001212 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001213 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001214 print "Loading target..."
1215 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1216 print "Loading source..."
1217 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1218
1219 self.verbatim_targets = verbatim_targets = []
1220 self.patch_list = patch_list = []
1221 diffs = []
1222 self.renames = renames = {}
1223 known_paths = set()
1224 largest_source_size = 0
1225
1226 matching_file_cache = {}
1227 for fn, sf in source_data.items():
1228 assert fn == sf.name
1229 matching_file_cache["path:" + fn] = sf
1230 if fn in target_data.keys():
1231 AddToKnownPaths(fn, known_paths)
1232 # Only allow eligibility for filename/sha matching
1233 # if there isn't a perfect path match.
1234 if target_data.get(sf.name) is None:
1235 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1236 matching_file_cache["sha:" + sf.sha1] = sf
1237
1238 for fn in sorted(target_data.keys()):
1239 tf = target_data[fn]
1240 assert fn == tf.name
1241 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1242 if sf is not None and sf.name != tf.name:
1243 print "File has moved from " + sf.name + " to " + tf.name
1244 renames[sf.name] = tf
1245
1246 if sf is None or fn in OPTIONS.require_verbatim:
1247 # This file should be included verbatim
1248 if fn in OPTIONS.prohibit_verbatim:
1249 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1250 print "send", fn, "verbatim"
1251 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001252 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001253 if fn in target_data.keys():
1254 AddToKnownPaths(fn, known_paths)
1255 elif tf.sha1 != sf.sha1:
1256 # File is different; consider sending as a patch
1257 diffs.append(common.Difference(tf, sf))
1258 else:
1259 # Target file data identical to source (may still be renamed)
1260 pass
1261
1262 common.ComputeDifferences(diffs)
1263
1264 for diff in diffs:
1265 tf, sf, d = diff.GetPatch()
1266 path = "/".join(tf.name.split("/")[:-1])
1267 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1268 path not in known_paths:
1269 # patch is almost as big as the file; don't bother patching
1270 # or a patch + rename cannot take place due to the target
1271 # directory not existing
1272 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001273 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001274 if sf.name in renames:
1275 del renames[sf.name]
1276 AddToKnownPaths(tf.name, known_paths)
1277 else:
1278 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1279 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1280 largest_source_size = max(largest_source_size, sf.size)
1281
1282 self.largest_source_size = largest_source_size
1283
1284 def EmitVerification(self, script):
1285 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001286 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001287 if tf.name != sf.name:
1288 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1289 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1290 so_far += sf.size
1291 return so_far
1292
Michael Runge63f01de2014-10-28 19:24:19 -07001293 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001294 for fn, _, sha1 in self.verbatim_targets:
1295 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001296 script.FileCheck("/"+fn, sha1)
1297 for tf, _, _, _ in self.patch_list:
1298 script.FileCheck(tf.name, tf.sha1)
1299
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001300 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001301 file_list = ["/" + i[0] for i in self.verbatim_targets]
1302 file_list += ["/" + i for i in self.source_data
1303 if i not in self.target_data and i not in self.renames]
1304 file_list += list(extras)
1305 # Sort the list in descending order, which removes all the files first
1306 # before attempting to remove the folder. (Bug: 22960996)
1307 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001308
1309 def TotalPatchSize(self):
1310 return sum(i[1].size for i in self.patch_list)
1311
1312 def EmitPatches(self, script, total_patch_size, so_far):
1313 self.deferred_patch_list = deferred_patch_list = []
1314 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001315 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001316 if tf.name == "system/build.prop":
1317 deferred_patch_list.append(item)
1318 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001319 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001320 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001321 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1322 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001323 so_far += tf.size
1324 script.SetProgress(so_far / total_patch_size)
1325 return so_far
1326
1327 def EmitDeferredPatches(self, script):
1328 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001329 tf, sf, _, _ = item
1330 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1331 "patch/" + sf.name + ".p")
1332 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001333
1334 def EmitRenames(self, script):
1335 if len(self.renames) > 0:
1336 script.Print("Renaming files...")
1337 for src, tgt in self.renames.iteritems():
1338 print "Renaming " + src + " to " + tgt.name
1339 script.RenameFile(src, tgt.name)
1340
1341
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001342def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001343 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1344 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1345
Doug Zongker26e66192014-02-20 13:22:07 -08001346 if (OPTIONS.block_based and
1347 target_has_recovery_patch and
1348 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001349 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1350
Doug Zongker37974732010-09-16 17:44:38 -07001351 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1352 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001353
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001354 if source_version == 0:
1355 print ("WARNING: generating edify script for a source that "
1356 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001357 script = edify_generator.EdifyGenerator(
1358 source_version, OPTIONS.target_info_dict,
1359 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001360
Michael Runge6e836112014-04-15 17:40:21 -07001361 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001362 recovery_mount_options = OPTIONS.source_info_dict.get(
1363 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001364 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001365 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001366 if OPTIONS.oem_source is None:
1367 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -07001368 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001369 oem_dict = common.LoadDictionaryFromLines(
1370 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001371
Dan Albert8b72aef2015-03-23 19:13:21 -07001372 metadata = {
1373 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1374 OPTIONS.source_info_dict),
1375 "post-timestamp": GetBuildProp("ro.build.date.utc",
1376 OPTIONS.target_info_dict),
1377 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001378
Doug Zongker05d3dea2009-06-22 11:32:31 -07001379 device_specific = common.DeviceSpecificParams(
1380 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001381 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001382 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001383 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001384 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001385 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001386 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001387 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001388
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001389 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001390 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001391 if HasVendorPartition(target_zip):
1392 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001393 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001394 else:
1395 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001396
Dan Albert8b72aef2015-03-23 19:13:21 -07001397 target_fp = CalculateFingerprint(oem_props, oem_dict,
1398 OPTIONS.target_info_dict)
1399 source_fp = CalculateFingerprint(oem_props, oem_dict,
1400 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001401
1402 if oem_props is None:
1403 script.AssertSomeFingerprint(source_fp, target_fp)
1404 else:
1405 script.AssertSomeThumbprint(
1406 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1407 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1408
Doug Zongker2ea21062010-04-28 16:05:21 -07001409 metadata["pre-build"] = source_fp
1410 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001411
Doug Zongker55d93282011-01-25 17:03:34 -08001412 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001413 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1414 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001415 target_boot = common.GetBootableImage(
1416 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001417 updating_boot = (not OPTIONS.two_step and
1418 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001419
Doug Zongker55d93282011-01-25 17:03:34 -08001420 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001421 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1422 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001423 target_recovery = common.GetBootableImage(
1424 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001425 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001426
Doug Zongker881dd402009-09-20 14:03:55 -07001427 # Here's how we divide up the progress bar:
1428 # 0.1 for verifying the start state (PatchCheck calls)
1429 # 0.8 for applying patches (ApplyPatch calls)
1430 # 0.1 for unpacking verbatim files, symlinking, and doing the
1431 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001432
Michael Runge6e836112014-04-15 17:40:21 -07001433 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001434 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001435
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001436 # Two-step incremental package strategy (in chronological order,
1437 # which is *not* the order in which the generated script has
1438 # things):
1439 #
1440 # if stage is not "2/3" or "3/3":
1441 # do verification on current system
1442 # write recovery image to boot partition
1443 # set stage to "2/3"
1444 # reboot to boot partition and restart recovery
1445 # else if stage is "2/3":
1446 # write recovery image to recovery partition
1447 # set stage to "3/3"
1448 # reboot to recovery partition and restart recovery
1449 # else:
1450 # (stage must be "3/3")
1451 # perform update:
1452 # patch system files, etc.
1453 # force full install of new boot image
1454 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001455 # complete script normally
1456 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001457
1458 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001459 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001460 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001461 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001462 assert fs.fs_type.upper() == "EMMC", \
1463 "two-step packages only supported on devices with EMMC /misc partitions"
1464 bcb_dev = {"bcb_dev": fs.device}
1465 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1466 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001467if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001468""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001469 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001470 script.WriteRawImage("/recovery", "recovery.img")
1471 script.AppendExtra("""
1472set_stage("%(bcb_dev)s", "3/3");
1473reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001474else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001475""" % bcb_dev)
1476
Tao Bao6c55a8a2015-04-08 15:30:27 -07001477 # Dump fingerprints
1478 script.Print("Source: %s" % (source_fp,))
1479 script.Print("Target: %s" % (target_fp,))
1480
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001481 script.Print("Verifying current system...")
1482
Doug Zongkere5ff5902012-01-17 10:55:37 -08001483 device_specific.IncrementalOTA_VerifyBegin()
1484
Doug Zongker881dd402009-09-20 14:03:55 -07001485 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001486 so_far = system_diff.EmitVerification(script)
1487 if vendor_diff:
1488 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001489
Doug Zongker5da317e2009-06-02 13:38:17 -07001490 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001491 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001492 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001493 print "boot target: %d source: %d diff: %d" % (
1494 target_boot.size, source_boot.size, len(d))
1495
Doug Zongker048e7ca2009-06-15 14:31:53 -07001496 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001497
Tao Baodd24da92015-07-29 14:09:23 -07001498 boot_type, boot_device = common.GetTypeAndDevice(
1499 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001500
1501 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1502 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001503 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001504 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001505 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001506
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001507 size = []
Dan Albert8b72aef2015-03-23 19:13:21 -07001508 if system_diff.patch_list:
1509 size.append(system_diff.largest_source_size)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001510 if vendor_diff:
Dan Albert8b72aef2015-03-23 19:13:21 -07001511 if vendor_diff.patch_list:
1512 size.append(vendor_diff.largest_source_size)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001513 if size or updating_recovery or updating_boot:
1514 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001515
Doug Zongker05d3dea2009-06-22 11:32:31 -07001516 device_specific.IncrementalOTA_VerifyEnd()
1517
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001518 if OPTIONS.two_step:
1519 script.WriteRawImage("/boot", "recovery.img")
1520 script.AppendExtra("""
1521set_stage("%(bcb_dev)s", "2/3");
1522reboot_now("%(bcb_dev)s", "");
1523else
1524""" % bcb_dev)
1525
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001526 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001527
Doug Zongkere5ff5902012-01-17 10:55:37 -08001528 device_specific.IncrementalOTA_InstallBegin()
1529
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001530 if OPTIONS.two_step:
1531 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1532 script.WriteRawImage("/boot", "boot.img")
1533 print "writing full boot image (forced by two-step mode)"
1534
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001535 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001536 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1537 if vendor_diff:
1538 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001539
Doug Zongker881dd402009-09-20 14:03:55 -07001540 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001541 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1542 if vendor_diff:
1543 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001544 if updating_boot:
1545 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001546
1547 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001548 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1549 if vendor_diff:
1550 script.Print("Patching vendor files...")
1551 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001552
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001553 if not OPTIONS.two_step:
1554 if updating_boot:
1555 # Produce the boot image by applying a patch to the current
1556 # contents of the boot partition, and write it back to the
1557 # partition.
1558 script.Print("Patching boot image...")
1559 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1560 % (boot_type, boot_device,
1561 source_boot.size, source_boot.sha1,
1562 target_boot.size, target_boot.sha1),
1563 "-",
1564 target_boot.size, target_boot.sha1,
1565 source_boot.sha1, "patch/boot.img.p")
1566 so_far += target_boot.size
1567 script.SetProgress(so_far / total_patch_size)
1568 print "boot image changed; including."
1569 else:
1570 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001571
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001572 system_items = ItemSet("system", "META/filesystem_config.txt")
1573 if vendor_diff:
1574 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1575
Doug Zongkereef39442009-04-02 12:14:19 -07001576 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001577 # Recovery is generated as a patch using both the boot image
1578 # (which contains the same linux kernel as recovery) and the file
1579 # /system/etc/recovery-resource.dat (which contains all the images
1580 # used in the recovery UI) as sources. This lets us minimize the
1581 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001582 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001583 # For older builds where recovery-resource.dat is not present, we
1584 # use only the boot image as the source.
1585
Doug Zongkerc9253822014-02-04 12:17:58 -08001586 if not target_has_recovery_patch:
1587 def output_sink(fn, data):
1588 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001589 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001590
1591 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1592 target_recovery, target_boot)
1593 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001594 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001595 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001596 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001597 else:
1598 print "recovery image unchanged; skipping."
1599
Doug Zongker881dd402009-09-20 14:03:55 -07001600 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001601
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001602 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1603 if vendor_diff:
1604 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1605
1606 temp_script = script.MakeTemporary()
1607 system_items.GetMetadata(target_zip)
1608 system_items.Get("system").SetPermissions(temp_script)
1609 if vendor_diff:
1610 vendor_items.GetMetadata(target_zip)
1611 vendor_items.Get("vendor").SetPermissions(temp_script)
1612
1613 # Note that this call will mess up the trees of Items, so make sure
1614 # we're done with them.
1615 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1616 if vendor_diff:
1617 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001618
1619 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001620 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1621
1622 # Delete all the symlinks in source that aren't in target. This
1623 # needs to happen before verbatim files are unpacked, in case a
1624 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001625
1626 # If a symlink in the source will be replaced by a regular file, we cannot
1627 # delete the symlink/file in case the package gets applied again. For such
1628 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1629 # (Bug: 23646151)
1630 replaced_symlinks = dict()
1631 if system_diff:
1632 for i in system_diff.verbatim_targets:
1633 replaced_symlinks["/%s" % (i[0],)] = i[2]
1634 if vendor_diff:
1635 for i in vendor_diff.verbatim_targets:
1636 replaced_symlinks["/%s" % (i[0],)] = i[2]
1637
1638 if system_diff:
1639 for tf in system_diff.renames.values():
1640 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1641 if vendor_diff:
1642 for tf in vendor_diff.renames.values():
1643 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1644
1645 always_delete = []
1646 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001647 for dest, link in source_symlinks:
1648 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001649 if link in replaced_symlinks:
1650 may_delete.append((link, replaced_symlinks[link]))
1651 else:
1652 always_delete.append(link)
1653 script.DeleteFiles(always_delete)
1654 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001655
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001656 if system_diff.verbatim_targets:
1657 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001658 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001659 if vendor_diff and vendor_diff.verbatim_targets:
1660 script.Print("Unpacking new vendor files...")
1661 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001662
Doug Zongkerc9253822014-02-04 12:17:58 -08001663 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001664 script.Print("Unpacking new recovery...")
1665 script.UnpackPackageDir("recovery", "/system")
1666
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001667 system_diff.EmitRenames(script)
1668 if vendor_diff:
1669 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001670
Doug Zongker05d3dea2009-06-22 11:32:31 -07001671 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001672
1673 # Create all the symlinks that don't already exist, or point to
1674 # somewhere different than what we want. Delete each symlink before
1675 # creating it, since the 'symlink' command won't overwrite.
1676 to_create = []
1677 for dest, link in target_symlinks:
1678 if link in source_symlinks_d:
1679 if dest != source_symlinks_d[link]:
1680 to_create.append((dest, link))
1681 else:
1682 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001683 script.DeleteFiles([i[1] for i in to_create])
1684 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001685
1686 # Now that the symlinks are created, we can set all the
1687 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001688 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001689
Doug Zongker881dd402009-09-20 14:03:55 -07001690 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001691 device_specific.IncrementalOTA_InstallEnd()
1692
Doug Zongker1c390a22009-05-14 19:06:36 -07001693 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001694 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001695
Doug Zongkere92f15a2011-08-26 13:46:40 -07001696 # Patch the build.prop file last, so if something fails but the
1697 # device can still come up, it appears to be the old build and will
1698 # get set the OTA package again to retry.
1699 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001700 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001701
Doug Zongker922206e2014-03-04 13:16:24 -08001702 if OPTIONS.wipe_user_data:
1703 script.Print("Erasing user data...")
1704 script.FormatPartition("/data")
1705
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001706 if OPTIONS.two_step:
1707 script.AppendExtra("""
1708set_stage("%(bcb_dev)s", "");
1709endif;
1710endif;
1711""" % bcb_dev)
1712
Michael Runge63f01de2014-10-28 19:24:19 -07001713 if OPTIONS.verify and system_diff:
1714 script.Print("Remounting and verifying system partition files...")
1715 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001716 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001717 system_diff.EmitExplicitTargetVerification(script)
1718
1719 if OPTIONS.verify and vendor_diff:
1720 script.Print("Remounting and verifying vendor partition files...")
1721 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001722 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001723 vendor_diff.EmitExplicitTargetVerification(script)
Doug Zongker25568482014-03-03 10:21:27 -08001724 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001725
Doug Zongker2ea21062010-04-28 16:05:21 -07001726 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001727
1728
1729def main(argv):
1730
1731 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001732 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001733 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001734 elif o in ("-k", "--package_key"):
1735 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001736 elif o in ("-i", "--incremental_from"):
1737 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001738 elif o == "--full_radio":
1739 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001740 elif o == "--full_bootloader":
1741 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001742 elif o in ("-w", "--wipe_user_data"):
1743 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001744 elif o in ("-n", "--no_prereq"):
1745 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001746 elif o in ("-o", "--oem_settings"):
1747 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001748 elif o in ("-e", "--extra_script"):
1749 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001750 elif o in ("-a", "--aslr_mode"):
1751 if a in ("on", "On", "true", "True", "yes", "Yes"):
1752 OPTIONS.aslr_mode = True
1753 else:
1754 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001755 elif o in ("-t", "--worker_threads"):
1756 if a.isdigit():
1757 OPTIONS.worker_threads = int(a)
1758 else:
1759 raise ValueError("Cannot parse value %r for option %r - only "
1760 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001761 elif o in ("-2", "--two_step"):
1762 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001763 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001764 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001765 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001766 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001767 elif o == "--block":
1768 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001769 elif o in ("-b", "--binary"):
1770 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001771 elif o in ("--no_fallback_to_full",):
1772 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001773 elif o == "--stash_threshold":
1774 try:
1775 OPTIONS.stash_threshold = float(a)
1776 except ValueError:
1777 raise ValueError("Cannot parse value %r for option %r - expecting "
1778 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001779 elif o == "--gen_verify":
1780 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001781 elif o == "--log_diff":
1782 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001783 else:
1784 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001785 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001786
1787 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001788 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001789 extra_long_opts=[
1790 "board_config=",
1791 "package_key=",
1792 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001793 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001794 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001795 "wipe_user_data",
1796 "no_prereq",
1797 "extra_script=",
1798 "worker_threads=",
1799 "aslr_mode=",
1800 "two_step",
1801 "no_signing",
1802 "block",
1803 "binary=",
1804 "oem_settings=",
1805 "verify",
1806 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001807 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001808 "gen_verify",
1809 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001810 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001811
1812 if len(args) != 2:
1813 common.Usage(__doc__)
1814 sys.exit(1)
1815
Tao Baoc098e9e2016-01-07 13:03:56 -08001816 # Load the dict file from the zip directly to have a peek at the OTA type.
1817 # For packages using A/B update, unzipping is not needed.
1818 input_zip = zipfile.ZipFile(args[0], "r")
1819 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1820 common.ZipClose(input_zip)
1821
1822 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1823
1824 if ab_update:
1825 if OPTIONS.incremental_source is not None:
1826 OPTIONS.target_info_dict = OPTIONS.info_dict
1827 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1828 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1829 common.ZipClose(source_zip)
1830
1831 if OPTIONS.verbose:
1832 print "--- target info ---"
1833 common.DumpInfoDict(OPTIONS.info_dict)
1834
1835 if OPTIONS.incremental_source is not None:
1836 print "--- source info ---"
1837 common.DumpInfoDict(OPTIONS.source_info_dict)
1838
1839 WriteABOTAPackageWithBrilloScript(
1840 target_file=args[0],
1841 output_file=args[1],
1842 source_file=OPTIONS.incremental_source)
1843
1844 print "done."
1845 return
1846
Doug Zongker1c390a22009-05-14 19:06:36 -07001847 if OPTIONS.extra_script is not None:
1848 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1849
Doug Zongkereef39442009-04-02 12:14:19 -07001850 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001851 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001852
Doug Zongkereef39442009-04-02 12:14:19 -07001853 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001854 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001855
Doug Zongker37974732010-09-16 17:44:38 -07001856 if OPTIONS.verbose:
1857 print "--- target info ---"
1858 common.DumpInfoDict(OPTIONS.info_dict)
1859
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001860 # If the caller explicitly specified the device-specific extensions
1861 # path via -s/--device_specific, use that. Otherwise, use
1862 # META/releasetools.py if it is present in the target target_files.
1863 # Otherwise, take the path of the file from 'tool_extensions' in the
1864 # info dict and look for that in the local filesystem, relative to
1865 # the current directory.
1866
Doug Zongker37974732010-09-16 17:44:38 -07001867 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001868 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1869 if os.path.exists(from_input):
1870 print "(using device-specific extensions from target_files)"
1871 OPTIONS.device_specific = from_input
1872 else:
1873 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1874
Doug Zongker37974732010-09-16 17:44:38 -07001875 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001876 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001877
Tao Baoc098e9e2016-01-07 13:03:56 -08001878 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001879 raise common.ExternalError(
1880 "--- target build has specified no recovery ---")
1881
Tao Bao767e3ac2015-11-10 12:19:19 -08001882 # Use the default key to sign the package if not specified with package_key.
1883 if not OPTIONS.no_signing:
1884 if OPTIONS.package_key is None:
1885 OPTIONS.package_key = OPTIONS.info_dict.get(
1886 "default_system_dev_certificate",
1887 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001888
Tao Bao767e3ac2015-11-10 12:19:19 -08001889 # Set up the output zip. Create a temporary zip file if signing is needed.
1890 if OPTIONS.no_signing:
1891 if os.path.exists(args[1]):
1892 os.unlink(args[1])
1893 output_zip = zipfile.ZipFile(args[1], "w",
1894 compression=zipfile.ZIP_DEFLATED)
1895 else:
1896 temp_zip_file = tempfile.NamedTemporaryFile()
1897 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1898 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001899
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001900 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001901 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001902 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08001903 print "--- can't determine the cache partition size ---"
1904 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001905
Tao Bao9bc6bb22015-11-09 16:58:28 -08001906 # Generate a verify package.
1907 if OPTIONS.gen_verify:
1908 WriteVerifyPackage(input_zip, output_zip)
1909
Tao Bao767e3ac2015-11-10 12:19:19 -08001910 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001911 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001912 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001913
1914 # Generate an incremental OTA. It will fall back to generate a full OTA on
1915 # failure unless no_fallback_to_full is specified.
1916 else:
1917 print "unzipping source target-files..."
1918 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
1919 OPTIONS.incremental_source)
1920 OPTIONS.target_info_dict = OPTIONS.info_dict
1921 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1922 OPTIONS.source_tmp)
1923 if OPTIONS.verbose:
1924 print "--- source info ---"
1925 common.DumpInfoDict(OPTIONS.source_info_dict)
1926 try:
1927 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001928 if OPTIONS.log_diff:
1929 out_file = open(OPTIONS.log_diff, 'w')
1930 import target_files_diff
1931 target_files_diff.recursiveDiff('',
1932 OPTIONS.source_tmp,
1933 OPTIONS.input_tmp,
1934 out_file)
1935 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001936 except ValueError:
1937 if not OPTIONS.fallback_to_full:
1938 raise
1939 print "--- failed to build incremental; falling back to full ---"
1940 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001941 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001942
Tao Bao767e3ac2015-11-10 12:19:19 -08001943 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001944
Tao Bao767e3ac2015-11-10 12:19:19 -08001945 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001946 if not OPTIONS.no_signing:
1947 SignOutput(temp_zip_file.name, args[1])
1948 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001949
Doug Zongkereef39442009-04-02 12:14:19 -07001950 print "done."
1951
1952
1953if __name__ == '__main__':
1954 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001955 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001956 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001957 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07001958 print
1959 print " ERROR: %s" % (e,)
1960 print
1961 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001962 finally:
1963 common.Cleanup()