blob: bea33a39160925b9ffbbbfce7908139f0810eff7 [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
Tao Baod8d14be2016-02-04 14:26:02 -0800545 metadata["ota-type"] = "BLOCK" if block_based else "FILE"
546
Doug Zongker962069c2009-04-23 11:41:58 -0700547 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700548 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700549 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
550 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700551
Michael Runge6e836112014-04-15 17:40:21 -0700552 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700553 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800554
555 # Two-step package strategy (in chronological order, which is *not*
556 # the order in which the generated script has things):
557 #
558 # if stage is not "2/3" or "3/3":
559 # write recovery image to boot partition
560 # set stage to "2/3"
561 # reboot to boot partition and restart recovery
562 # else if stage is "2/3":
563 # write recovery image to recovery partition
564 # set stage to "3/3"
565 # reboot to recovery partition and restart recovery
566 # else:
567 # (stage must be "3/3")
568 # set stage to ""
569 # do normal full package installation:
570 # wipe and install system, boot image, etc.
571 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700572 # complete script normally
573 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800574
575 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
576 OPTIONS.input_tmp, "RECOVERY")
577 if OPTIONS.two_step:
578 if not OPTIONS.info_dict.get("multistage_support", None):
579 assert False, "two-step packages not supported by this build"
580 fs = OPTIONS.info_dict["fstab"]["/misc"]
581 assert fs.fs_type.upper() == "EMMC", \
582 "two-step packages only supported on devices with EMMC /misc partitions"
583 bcb_dev = {"bcb_dev": fs.device}
584 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
585 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700586if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800587""" % bcb_dev)
588 script.WriteRawImage("/recovery", "recovery.img")
589 script.AppendExtra("""
590set_stage("%(bcb_dev)s", "3/3");
591reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700592else if get_stage("%(bcb_dev)s") == "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800593""" % bcb_dev)
594
Tao Bao6c55a8a2015-04-08 15:30:27 -0700595 # Dump fingerprints
596 script.Print("Target: %s" % CalculateFingerprint(
597 oem_props, oem_dict, OPTIONS.info_dict))
598
Doug Zongkere5ff5902012-01-17 10:55:37 -0800599 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700600
Doug Zongker01ce19c2014-02-04 13:48:15 -0800601 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700602
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700603 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800604 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700605 if HasVendorPartition(input_zip):
606 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700607
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400608 # Place a copy of file_contexts.bin into the OTA package which will be used
609 # by the recovery program.
Kenny Rootf32dc712012-04-08 10:42:34 -0700610 if "selinux_fc" in OPTIONS.info_dict:
611 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500612
Michael Runge7cd99ba2014-10-22 17:21:48 -0700613 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
614
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700615 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700616 script.ShowProgress(system_progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -0800617
Doug Zongker26e66192014-02-20 13:22:07 -0800618 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700619 # Full OTA is done as an "incremental" against an empty source
620 # image. This has the effect of writing new data from the package
621 # to the entire partition, but lets us reuse the updater code that
622 # writes incrementals to do it.
623 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
624 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700625 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700626 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800627 else:
628 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700629 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800630 if not has_recovery_patch:
631 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800632 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700633
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700634 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800635 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700636
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700637 boot_img = common.GetBootableImage(
638 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800639
Doug Zongker91a99c22014-05-09 13:15:01 -0700640 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800641 def output_sink(fn, data):
642 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -0700643 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800644
645 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
646 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700647
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700648 system_items.GetMetadata(input_zip)
649 system_items.Get("system").SetPermissions(script)
650
651 if HasVendorPartition(input_zip):
652 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
653 script.ShowProgress(0.1, 0)
654
655 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700656 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
657 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700658 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700659 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700660 else:
661 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700662 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700663 script.UnpackPackageDir("vendor", "/vendor")
664
665 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
666 script.MakeSymlinks(symlinks)
667
668 vendor_items.GetMetadata(input_zip)
669 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700670
Doug Zongker37974732010-09-16 17:44:38 -0700671 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700672 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700673
Doug Zongker01ce19c2014-02-04 13:48:15 -0800674 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700675 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700676
Doug Zongker01ce19c2014-02-04 13:48:15 -0800677 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700678 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700679
Doug Zongker1c390a22009-05-14 19:06:36 -0700680 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700681 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700682
Doug Zongker14833602010-02-02 13:12:04 -0800683 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800684
Doug Zongker922206e2014-03-04 13:16:24 -0800685 if OPTIONS.wipe_user_data:
686 script.ShowProgress(0.1, 10)
687 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700688
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800689 if OPTIONS.two_step:
690 script.AppendExtra("""
691set_stage("%(bcb_dev)s", "");
692""" % bcb_dev)
693 script.AppendExtra("else\n")
694 script.WriteRawImage("/boot", "recovery.img")
695 script.AppendExtra("""
696set_stage("%(bcb_dev)s", "2/3");
697reboot_now("%(bcb_dev)s", "");
698endif;
699endif;
700""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800701 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -0800702
703 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -0700704 WriteMetadata(metadata, output_zip)
705
Doug Zongkerfc44a512014-08-26 13:10:25 -0700706
Dan Albert8e0178d2015-01-27 15:53:15 -0800707def WritePolicyConfig(file_name, output_zip):
708 common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
Stephen Smalley56882bf2012-02-09 13:36:21 -0500709
Doug Zongker2ea21062010-04-28 16:05:21 -0700710
711def WriteMetadata(metadata, output_zip):
712 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
713 "".join(["%s=%s\n" % kv
714 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700715
Doug Zongkerfc44a512014-08-26 13:10:25 -0700716
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700717def LoadPartitionFiles(z, partition):
718 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700719 ZipFile, and return a dict of {filename: File object}."""
720 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700721 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700722 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700723 if info.filename.startswith(prefix) and not IsSymlink(info):
Tao Baoeaf885b2015-03-23 16:01:17 -0700724 basefilename = info.filename[len(prefix):]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700725 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700726 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700727 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800728 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700729
730
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700731def GetBuildProp(prop, info_dict):
732 """Return the fingerprint of the build of a given target-files info_dict."""
733 try:
734 return info_dict.get("build.prop", {})[prop]
735 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700736 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700737
Doug Zongkerfc44a512014-08-26 13:10:25 -0700738
Michael Runge4038aa82013-12-13 18:06:28 -0800739def AddToKnownPaths(filename, known_paths):
740 if filename[-1] == "/":
741 return
742 dirs = filename.split("/")[:-1]
743 while len(dirs) > 0:
744 path = "/".join(dirs)
745 if path in known_paths:
Dan Albert8b72aef2015-03-23 19:13:21 -0700746 break
Michael Runge4038aa82013-12-13 18:06:28 -0800747 known_paths.add(path)
748 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700749
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700750
Geremy Condra36bd3652014-02-06 19:45:10 -0800751def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
Tao Bao3806c232015-07-05 21:08:33 -0700752 # TODO(tbao): We should factor out the common parts between
753 # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
Geremy Condra36bd3652014-02-06 19:45:10 -0800754 source_version = OPTIONS.source_info_dict["recovery_api_version"]
755 target_version = OPTIONS.target_info_dict["recovery_api_version"]
756
757 if source_version == 0:
758 print ("WARNING: generating edify script for a source that "
759 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -0700760 script = edify_generator.EdifyGenerator(
761 source_version, OPTIONS.target_info_dict,
762 fstab=OPTIONS.source_info_dict["fstab"])
Geremy Condra36bd3652014-02-06 19:45:10 -0800763
Tao Bao3806c232015-07-05 21:08:33 -0700764 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
765 recovery_mount_options = OPTIONS.source_info_dict.get(
766 "recovery_mount_options")
767 oem_dict = None
768 if oem_props is not None and len(oem_props) > 0:
769 if OPTIONS.oem_source is None:
770 raise common.ExternalError("OEM source required for this build")
771 script.Mount("/oem", recovery_mount_options)
772 oem_dict = common.LoadDictionaryFromLines(
773 open(OPTIONS.oem_source).readlines())
774
Dan Albert8b72aef2015-03-23 19:13:21 -0700775 metadata = {
Tao Bao3806c232015-07-05 21:08:33 -0700776 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
777 OPTIONS.source_info_dict),
Dan Albert8b72aef2015-03-23 19:13:21 -0700778 "post-timestamp": GetBuildProp("ro.build.date.utc",
779 OPTIONS.target_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -0800780 "ota-type": "BLOCK",
Dan Albert8b72aef2015-03-23 19:13:21 -0700781 }
Geremy Condra36bd3652014-02-06 19:45:10 -0800782
783 device_specific = common.DeviceSpecificParams(
784 source_zip=source_zip,
785 source_version=source_version,
786 target_zip=target_zip,
787 target_version=target_version,
788 output_zip=output_zip,
789 script=script,
790 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -0700791 info_dict=OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800792
Tao Bao3806c232015-07-05 21:08:33 -0700793 source_fp = CalculateFingerprint(oem_props, oem_dict,
794 OPTIONS.source_info_dict)
795 target_fp = CalculateFingerprint(oem_props, oem_dict,
796 OPTIONS.target_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800797 metadata["pre-build"] = source_fp
798 metadata["post-build"] = target_fp
799
800 source_boot = common.GetBootableImage(
801 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
802 OPTIONS.source_info_dict)
803 target_boot = common.GetBootableImage(
804 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
805 updating_boot = (not OPTIONS.two_step and
806 (source_boot.data != target_boot.data))
807
Geremy Condra36bd3652014-02-06 19:45:10 -0800808 target_recovery = common.GetBootableImage(
809 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Geremy Condra36bd3652014-02-06 19:45:10 -0800810
Doug Zongkerfc44a512014-08-26 13:10:25 -0700811 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
812 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Tao Baodd2a5892015-03-12 12:32:37 -0700813
814 blockimgdiff_version = 1
815 if OPTIONS.info_dict:
816 blockimgdiff_version = max(
817 int(i) for i in
818 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
819
Tianjie Xufc3422a2015-12-15 11:53:59 -0800820 # Check first block of system partition for remount R/W only if
821 # disk type is ext4
822 system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
Tao Baod8d14be2016-02-04 14:26:02 -0800823 check_first_block = system_partition.fs_type == "ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700824 system_diff = common.BlockDifference("system", system_tgt, system_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800825 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700826 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700827
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700828 if HasVendorPartition(target_zip):
829 if not HasVendorPartition(source_zip):
830 raise RuntimeError("can't generate incremental that adds /vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700831 vendor_src = GetImage("vendor", OPTIONS.source_tmp,
832 OPTIONS.source_info_dict)
833 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp,
834 OPTIONS.target_info_dict)
Tianjie Xufc3422a2015-12-15 11:53:59 -0800835
836 # Check first block of vendor partition for remount R/W only if
837 # disk type is ext4
838 vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
Tao Baod8d14be2016-02-04 14:26:02 -0800839 check_first_block = vendor_partition.fs_type == "ext4"
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700840 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
Tianjie Xufc3422a2015-12-15 11:53:59 -0800841 check_first_block,
Tao Baodd2a5892015-03-12 12:32:37 -0700842 version=blockimgdiff_version)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700843 else:
844 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800845
Michael Rungec6e3afd2014-05-05 11:55:47 -0700846 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800847 device_specific.IncrementalOTA_Assertions()
848
849 # Two-step incremental package strategy (in chronological order,
850 # which is *not* the order in which the generated script has
851 # things):
852 #
853 # if stage is not "2/3" or "3/3":
854 # do verification on current system
855 # write recovery image to boot partition
856 # set stage to "2/3"
857 # reboot to boot partition and restart recovery
858 # else if stage is "2/3":
859 # write recovery image to recovery partition
860 # set stage to "3/3"
861 # reboot to recovery partition and restart recovery
862 # else:
863 # (stage must be "3/3")
864 # perform update:
865 # patch system files, etc.
866 # force full install of new boot image
867 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -0700868 # complete script normally
869 # (allow recovery to mark itself finished and reboot)
Geremy Condra36bd3652014-02-06 19:45:10 -0800870
871 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -0700872 if not OPTIONS.source_info_dict.get("multistage_support", None):
Geremy Condra36bd3652014-02-06 19:45:10 -0800873 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -0700874 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Geremy Condra36bd3652014-02-06 19:45:10 -0800875 assert fs.fs_type.upper() == "EMMC", \
876 "two-step packages only supported on devices with EMMC /misc partitions"
877 bcb_dev = {"bcb_dev": fs.device}
878 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
879 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -0700880if get_stage("%(bcb_dev)s") == "2/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800881""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -0700882 script.AppendExtra("sleep(20);\n")
Geremy Condra36bd3652014-02-06 19:45:10 -0800883 script.WriteRawImage("/recovery", "recovery.img")
884 script.AppendExtra("""
885set_stage("%(bcb_dev)s", "3/3");
886reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -0700887else if get_stage("%(bcb_dev)s") != "3/3" then
Geremy Condra36bd3652014-02-06 19:45:10 -0800888""" % bcb_dev)
889
Tao Bao6c55a8a2015-04-08 15:30:27 -0700890 # Dump fingerprints
891 script.Print("Source: %s" % CalculateFingerprint(
892 oem_props, oem_dict, OPTIONS.source_info_dict))
893 script.Print("Target: %s" % CalculateFingerprint(
894 oem_props, oem_dict, OPTIONS.target_info_dict))
895
Geremy Condra36bd3652014-02-06 19:45:10 -0800896 script.Print("Verifying current system...")
897
898 device_specific.IncrementalOTA_VerifyBegin()
899
Michael Rungec6e3afd2014-05-05 11:55:47 -0700900 if oem_props is None:
Tao Baodd2a5892015-03-12 12:32:37 -0700901 # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
902 # patching on a device that's already on the target build will damage the
903 # system. Because operations like move don't check the block state, they
904 # always apply the changes unconditionally.
905 if blockimgdiff_version <= 2:
906 script.AssertSomeFingerprint(source_fp)
907 else:
908 script.AssertSomeFingerprint(source_fp, target_fp)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700909 else:
Tao Baodd2a5892015-03-12 12:32:37 -0700910 if blockimgdiff_version <= 2:
911 script.AssertSomeThumbprint(
912 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
913 else:
914 script.AssertSomeThumbprint(
915 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
916 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800917
Tao Baod8d14be2016-02-04 14:26:02 -0800918 # Check the required cache size (i.e. stashed blocks).
919 size = []
920 if system_diff:
921 size.append(system_diff.required_cache)
922 if vendor_diff:
923 size.append(vendor_diff.required_cache)
924
Geremy Condra36bd3652014-02-06 19:45:10 -0800925 if updating_boot:
Tao Baodd24da92015-07-29 14:09:23 -0700926 boot_type, boot_device = common.GetTypeAndDevice(
927 "/boot", OPTIONS.source_info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800928 d = common.Difference(target_boot, source_boot)
929 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700930 if d is None:
931 include_full_boot = True
932 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
933 else:
934 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800935
Doug Zongkerf8340082014-08-05 10:39:37 -0700936 print "boot target: %d source: %d diff: %d" % (
937 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800938
Doug Zongkerf8340082014-08-05 10:39:37 -0700939 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800940
Doug Zongkerf8340082014-08-05 10:39:37 -0700941 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
942 (boot_type, boot_device,
943 source_boot.size, source_boot.sha1,
944 target_boot.size, target_boot.sha1))
Tao Baod8d14be2016-02-04 14:26:02 -0800945 size.append(target_boot.size)
946
947 if size:
948 script.CacheFreeSpaceCheck(max(size))
Geremy Condra36bd3652014-02-06 19:45:10 -0800949
950 device_specific.IncrementalOTA_VerifyEnd()
951
952 if OPTIONS.two_step:
953 script.WriteRawImage("/boot", "recovery.img")
954 script.AppendExtra("""
955set_stage("%(bcb_dev)s", "2/3");
956reboot_now("%(bcb_dev)s", "");
957else
958""" % bcb_dev)
959
Jesse Zhao75bcea02015-01-06 10:59:53 -0800960 # Verify the existing partitions.
961 system_diff.WriteVerifyScript(script)
962 if vendor_diff:
963 vendor_diff.WriteVerifyScript(script)
964
Geremy Condra36bd3652014-02-06 19:45:10 -0800965 script.Comment("---- start making changes here ----")
966
967 device_specific.IncrementalOTA_InstallBegin()
968
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700969 system_diff.WriteScript(script, output_zip,
970 progress=0.8 if vendor_diff else 0.9)
Tao Bao68658c02015-06-01 13:40:49 -0700971
Doug Zongkerfc44a512014-08-26 13:10:25 -0700972 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700973 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800974
975 if OPTIONS.two_step:
976 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
977 script.WriteRawImage("/boot", "boot.img")
978 print "writing full boot image (forced by two-step mode)"
979
980 if not OPTIONS.two_step:
981 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700982 if include_full_boot:
983 print "boot image changed; including full."
984 script.Print("Installing boot image...")
985 script.WriteRawImage("/boot", "boot.img")
986 else:
987 # Produce the boot image by applying a patch to the current
988 # contents of the boot partition, and write it back to the
989 # partition.
990 print "boot image changed; including patch."
991 script.Print("Patching boot image...")
992 script.ShowProgress(0.1, 10)
993 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
994 % (boot_type, boot_device,
995 source_boot.size, source_boot.sha1,
996 target_boot.size, target_boot.sha1),
997 "-",
998 target_boot.size, target_boot.sha1,
999 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -08001000 else:
1001 print "boot image unchanged; skipping."
1002
1003 # Do device-specific installation (eg, write radio image).
1004 device_specific.IncrementalOTA_InstallEnd()
1005
1006 if OPTIONS.extra_script is not None:
1007 script.AppendExtra(OPTIONS.extra_script)
1008
Doug Zongker922206e2014-03-04 13:16:24 -08001009 if OPTIONS.wipe_user_data:
1010 script.Print("Erasing user data...")
1011 script.FormatPartition("/data")
1012
Geremy Condra36bd3652014-02-06 19:45:10 -08001013 if OPTIONS.two_step:
1014 script.AppendExtra("""
1015set_stage("%(bcb_dev)s", "");
1016endif;
1017endif;
1018""" % bcb_dev)
1019
1020 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -08001021 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001022 metadata["ota-required-cache"] = str(script.required_cache)
Geremy Condra36bd3652014-02-06 19:45:10 -08001023 WriteMetadata(metadata, output_zip)
1024
Doug Zongker32b527d2014-03-04 10:03:02 -08001025
Tao Bao9bc6bb22015-11-09 16:58:28 -08001026def WriteVerifyPackage(input_zip, output_zip):
1027 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
1028
1029 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1030 recovery_mount_options = OPTIONS.info_dict.get(
1031 "recovery_mount_options")
1032 oem_dict = None
1033 if oem_props is not None and len(oem_props) > 0:
1034 if OPTIONS.oem_source is None:
1035 raise common.ExternalError("OEM source required for this build")
1036 script.Mount("/oem", recovery_mount_options)
1037 oem_dict = common.LoadDictionaryFromLines(
1038 open(OPTIONS.oem_source).readlines())
1039
1040 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.info_dict)
1041 metadata = {
1042 "post-build": target_fp,
1043 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1044 OPTIONS.info_dict),
1045 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
1046 }
1047
1048 device_specific = common.DeviceSpecificParams(
1049 input_zip=input_zip,
1050 input_version=OPTIONS.info_dict["recovery_api_version"],
1051 output_zip=output_zip,
1052 script=script,
1053 input_tmp=OPTIONS.input_tmp,
1054 metadata=metadata,
1055 info_dict=OPTIONS.info_dict)
1056
1057 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
1058
1059 script.Print("Verifying device images against %s..." % target_fp)
1060 script.AppendExtra("")
1061
1062 script.Print("Verifying boot...")
1063 boot_img = common.GetBootableImage(
1064 "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
1065 boot_type, boot_device = common.GetTypeAndDevice(
1066 "/boot", OPTIONS.info_dict)
1067 script.Verify("%s:%s:%d:%s" % (
1068 boot_type, boot_device, boot_img.size, boot_img.sha1))
1069 script.AppendExtra("")
1070
1071 script.Print("Verifying recovery...")
1072 recovery_img = common.GetBootableImage(
1073 "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1074 recovery_type, recovery_device = common.GetTypeAndDevice(
1075 "/recovery", OPTIONS.info_dict)
1076 script.Verify("%s:%s:%d:%s" % (
1077 recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
1078 script.AppendExtra("")
1079
1080 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
1081 system_tgt.ResetFileMap()
1082 system_diff = common.BlockDifference("system", system_tgt, src=None)
1083 system_diff.WriteStrictVerifyScript(script)
1084
1085 if HasVendorPartition(input_zip):
1086 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
1087 vendor_tgt.ResetFileMap()
1088 vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
1089 vendor_diff.WriteStrictVerifyScript(script)
1090
1091 # Device specific partitions, such as radio, bootloader and etc.
1092 device_specific.VerifyOTA_Assertions()
1093
1094 script.SetProgress(1.0)
1095 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Tao Baod8d14be2016-02-04 14:26:02 -08001096 metadata["ota-required-cache"] = str(script.required_cache)
Tao Bao9bc6bb22015-11-09 16:58:28 -08001097 WriteMetadata(metadata, output_zip)
1098
1099
Tao Baoc098e9e2016-01-07 13:03:56 -08001100def WriteABOTAPackageWithBrilloScript(target_file, output_file,
1101 source_file=None):
1102 """Generate an Android OTA package that has A/B update payload."""
1103
1104 # Setup signing keys.
1105 if OPTIONS.package_key is None:
1106 OPTIONS.package_key = OPTIONS.info_dict.get(
1107 "default_system_dev_certificate",
1108 "build/target/product/security/testkey")
1109
1110 # A/B updater expects key in RSA format.
1111 cmd = ["openssl", "pkcs8",
1112 "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
1113 "-inform", "DER", "-nocrypt"]
1114 rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
1115 cmd.extend(["-out", rsa_key])
1116 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1117 p1.wait()
1118 assert p1.returncode == 0, "openssl pkcs8 failed"
1119
1120 # Stage the output zip package for signing.
1121 temp_zip_file = tempfile.NamedTemporaryFile()
1122 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1123 compression=zipfile.ZIP_DEFLATED)
1124
1125 # Metadata to comply with Android OTA package format.
1126 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
1127 oem_dict = None
1128 if oem_props:
1129 if OPTIONS.oem_source is None:
1130 raise common.ExternalError("OEM source required for this build")
1131 oem_dict = common.LoadDictionaryFromLines(
1132 open(OPTIONS.oem_source).readlines())
1133
1134 metadata = {
1135 "post-build": CalculateFingerprint(oem_props, oem_dict,
1136 OPTIONS.info_dict),
1137 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1138 OPTIONS.info_dict),
1139 "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001140 "ota-required-cache": "0",
1141 "ota-type": "AB",
Tao Baoc098e9e2016-01-07 13:03:56 -08001142 }
1143
1144 if source_file is not None:
1145 metadata["pre-build"] = CalculateFingerprint(oem_props, oem_dict,
1146 OPTIONS.source_info_dict)
1147
1148 # 1. Generate payload.
1149 payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
1150 cmd = ["brillo_update_payload", "generate",
1151 "--payload", payload_file,
1152 "--target_image", target_file]
1153 if source_file is not None:
1154 cmd.extend(["--source_image", source_file])
1155 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1156 p1.wait()
1157 assert p1.returncode == 0, "brillo_update_payload generate failed"
1158
1159 # 2. Generate hashes of the payload and metadata files.
1160 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1161 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
1162 cmd = ["brillo_update_payload", "hash",
1163 "--unsigned_payload", payload_file,
1164 "--signature_size", "256",
1165 "--metadata_hash_file", metadata_sig_file,
1166 "--payload_hash_file", payload_sig_file]
1167 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1168 p1.wait()
1169 assert p1.returncode == 0, "brillo_update_payload hash failed"
1170
1171 # 3. Sign the hashes and insert them back into the payload file.
1172 signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
1173 suffix=".bin")
1174 signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
1175 suffix=".bin")
1176 # 3a. Sign the payload hash.
1177 cmd = ["openssl", "pkeyutl", "-sign",
1178 "-inkey", rsa_key,
1179 "-pkeyopt", "digest:sha256",
1180 "-in", payload_sig_file,
1181 "-out", signed_payload_sig_file]
1182 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1183 p1.wait()
1184 assert p1.returncode == 0, "openssl sign payload failed"
1185
1186 # 3b. Sign the metadata hash.
1187 cmd = ["openssl", "pkeyutl", "-sign",
1188 "-inkey", rsa_key,
1189 "-pkeyopt", "digest:sha256",
1190 "-in", metadata_sig_file,
1191 "-out", signed_metadata_sig_file]
1192 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1193 p1.wait()
1194 assert p1.returncode == 0, "openssl sign metadata failed"
1195
1196 # 3c. Insert the signatures back into the payload file.
1197 signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
1198 suffix=".bin")
1199 cmd = ["brillo_update_payload", "sign",
1200 "--unsigned_payload", payload_file,
1201 "--payload", signed_payload_file,
1202 "--signature_size", "256",
1203 "--metadata_signature_file", signed_metadata_sig_file,
1204 "--payload_signature_file", signed_payload_sig_file]
1205 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1206 p1.wait()
1207 assert p1.returncode == 0, "brillo_update_payload sign failed"
1208
Alex Deymo19241c12016-02-04 22:29:29 -08001209 # 4. Dump the signed payload properties.
1210 properties_file = common.MakeTempFile(prefix="payload-properties-",
1211 suffix=".txt")
1212 cmd = ["brillo_update_payload", "properties",
1213 "--payload", signed_payload_file,
1214 "--properties_file", properties_file]
1215 p1 = common.Run(cmd, stdout=subprocess.PIPE)
1216 p1.wait()
1217 assert p1.returncode == 0, "brillo_update_payload properties failed"
1218
1219 # Add the signed payload file and properties into the zip.
1220 common.ZipWrite(output_zip, properties_file, arcname="payload_properties.txt")
Tao Baoc098e9e2016-01-07 13:03:56 -08001221 common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
1222 compress_type=zipfile.ZIP_STORED)
1223 WriteMetadata(metadata, output_zip)
1224
1225 # Sign the whole package to comply with the Android OTA package format.
1226 common.ZipClose(output_zip)
1227 SignOutput(temp_zip_file.name, output_file)
1228 temp_zip_file.close()
1229
1230
Dan Albert8b72aef2015-03-23 19:13:21 -07001231class FileDifference(object):
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001232 def __init__(self, partition, source_zip, target_zip, output_zip):
Dan Albert8b72aef2015-03-23 19:13:21 -07001233 self.deferred_patch_list = None
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001234 print "Loading target..."
1235 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1236 print "Loading source..."
1237 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1238
1239 self.verbatim_targets = verbatim_targets = []
1240 self.patch_list = patch_list = []
1241 diffs = []
1242 self.renames = renames = {}
1243 known_paths = set()
1244 largest_source_size = 0
1245
1246 matching_file_cache = {}
1247 for fn, sf in source_data.items():
1248 assert fn == sf.name
1249 matching_file_cache["path:" + fn] = sf
1250 if fn in target_data.keys():
1251 AddToKnownPaths(fn, known_paths)
1252 # Only allow eligibility for filename/sha matching
1253 # if there isn't a perfect path match.
1254 if target_data.get(sf.name) is None:
1255 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1256 matching_file_cache["sha:" + sf.sha1] = sf
1257
1258 for fn in sorted(target_data.keys()):
1259 tf = target_data[fn]
1260 assert fn == tf.name
1261 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1262 if sf is not None and sf.name != tf.name:
1263 print "File has moved from " + sf.name + " to " + tf.name
1264 renames[sf.name] = tf
1265
1266 if sf is None or fn in OPTIONS.require_verbatim:
1267 # This file should be included verbatim
1268 if fn in OPTIONS.prohibit_verbatim:
1269 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1270 print "send", fn, "verbatim"
1271 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001272 verbatim_targets.append((fn, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001273 if fn in target_data.keys():
1274 AddToKnownPaths(fn, known_paths)
1275 elif tf.sha1 != sf.sha1:
1276 # File is different; consider sending as a patch
1277 diffs.append(common.Difference(tf, sf))
1278 else:
1279 # Target file data identical to source (may still be renamed)
1280 pass
1281
1282 common.ComputeDifferences(diffs)
1283
1284 for diff in diffs:
1285 tf, sf, d = diff.GetPatch()
1286 path = "/".join(tf.name.split("/")[:-1])
1287 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1288 path not in known_paths:
1289 # patch is almost as big as the file; don't bother patching
1290 # or a patch + rename cannot take place due to the target
1291 # directory not existing
1292 tf.AddToZip(output_zip)
Michael Runge63f01de2014-10-28 19:24:19 -07001293 verbatim_targets.append((tf.name, tf.size, tf.sha1))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001294 if sf.name in renames:
1295 del renames[sf.name]
1296 AddToKnownPaths(tf.name, known_paths)
1297 else:
1298 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1299 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1300 largest_source_size = max(largest_source_size, sf.size)
1301
1302 self.largest_source_size = largest_source_size
1303
1304 def EmitVerification(self, script):
1305 so_far = 0
Dan Albert8b72aef2015-03-23 19:13:21 -07001306 for tf, sf, _, _ in self.patch_list:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001307 if tf.name != sf.name:
1308 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1309 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1310 so_far += sf.size
1311 return so_far
1312
Michael Runge63f01de2014-10-28 19:24:19 -07001313 def EmitExplicitTargetVerification(self, script):
Dan Albert8b72aef2015-03-23 19:13:21 -07001314 for fn, _, sha1 in self.verbatim_targets:
1315 if fn[-1] != "/":
Michael Runge63f01de2014-10-28 19:24:19 -07001316 script.FileCheck("/"+fn, sha1)
1317 for tf, _, _, _ in self.patch_list:
1318 script.FileCheck(tf.name, tf.sha1)
1319
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001320 def RemoveUnneededFiles(self, script, extras=()):
Tao Baoa77d41e2015-09-03 21:17:37 -07001321 file_list = ["/" + i[0] for i in self.verbatim_targets]
1322 file_list += ["/" + i for i in self.source_data
1323 if i not in self.target_data and i not in self.renames]
1324 file_list += list(extras)
1325 # Sort the list in descending order, which removes all the files first
1326 # before attempting to remove the folder. (Bug: 22960996)
1327 script.DeleteFiles(sorted(file_list, reverse=True))
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001328
1329 def TotalPatchSize(self):
1330 return sum(i[1].size for i in self.patch_list)
1331
1332 def EmitPatches(self, script, total_patch_size, so_far):
1333 self.deferred_patch_list = deferred_patch_list = []
1334 for item in self.patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001335 tf, sf, _, _ = item
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001336 if tf.name == "system/build.prop":
1337 deferred_patch_list.append(item)
1338 continue
Dan Albert8b72aef2015-03-23 19:13:21 -07001339 if sf.name != tf.name:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001340 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
Dan Albert8b72aef2015-03-23 19:13:21 -07001341 script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
1342 "patch/" + sf.name + ".p")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001343 so_far += tf.size
1344 script.SetProgress(so_far / total_patch_size)
1345 return so_far
1346
1347 def EmitDeferredPatches(self, script):
1348 for item in self.deferred_patch_list:
Dan Albert8b72aef2015-03-23 19:13:21 -07001349 tf, sf, _, _ = item
1350 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
1351 "patch/" + sf.name + ".p")
1352 script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001353
1354 def EmitRenames(self, script):
1355 if len(self.renames) > 0:
1356 script.Print("Renaming files...")
1357 for src, tgt in self.renames.iteritems():
1358 print "Renaming " + src + " to " + tgt.name
1359 script.RenameFile(src, tgt.name)
1360
1361
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001362def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001363 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1364 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1365
Doug Zongker26e66192014-02-20 13:22:07 -08001366 if (OPTIONS.block_based and
1367 target_has_recovery_patch and
1368 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001369 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1370
Doug Zongker37974732010-09-16 17:44:38 -07001371 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1372 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001373
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001374 if source_version == 0:
1375 print ("WARNING: generating edify script for a source that "
1376 "can't install it.")
Tao Bao34b47bf2015-06-22 19:17:41 -07001377 script = edify_generator.EdifyGenerator(
1378 source_version, OPTIONS.target_info_dict,
1379 fstab=OPTIONS.source_info_dict["fstab"])
Doug Zongkereef39442009-04-02 12:14:19 -07001380
Michael Runge6e836112014-04-15 17:40:21 -07001381 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Tao Bao34b47bf2015-06-22 19:17:41 -07001382 recovery_mount_options = OPTIONS.source_info_dict.get(
1383 "recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001384 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001385 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001386 if OPTIONS.oem_source is None:
1387 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -07001388 script.Mount("/oem", recovery_mount_options)
Dan Albert8b72aef2015-03-23 19:13:21 -07001389 oem_dict = common.LoadDictionaryFromLines(
1390 open(OPTIONS.oem_source).readlines())
Michael Runge6e836112014-04-15 17:40:21 -07001391
Dan Albert8b72aef2015-03-23 19:13:21 -07001392 metadata = {
1393 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
1394 OPTIONS.source_info_dict),
1395 "post-timestamp": GetBuildProp("ro.build.date.utc",
1396 OPTIONS.target_info_dict),
Tao Baod8d14be2016-02-04 14:26:02 -08001397 "ota-type": "FILE",
Dan Albert8b72aef2015-03-23 19:13:21 -07001398 }
Doug Zongker2ea21062010-04-28 16:05:21 -07001399
Doug Zongker05d3dea2009-06-22 11:32:31 -07001400 device_specific = common.DeviceSpecificParams(
1401 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001402 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001403 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001404 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001405 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001406 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001407 metadata=metadata,
Tao Bao6f0b2192015-10-13 16:37:12 -07001408 info_dict=OPTIONS.source_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001409
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001410 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001411 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001412 if HasVendorPartition(target_zip):
1413 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001414 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001415 else:
1416 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001417
Dan Albert8b72aef2015-03-23 19:13:21 -07001418 target_fp = CalculateFingerprint(oem_props, oem_dict,
1419 OPTIONS.target_info_dict)
1420 source_fp = CalculateFingerprint(oem_props, oem_dict,
1421 OPTIONS.source_info_dict)
Michael Runge6e836112014-04-15 17:40:21 -07001422
1423 if oem_props is None:
1424 script.AssertSomeFingerprint(source_fp, target_fp)
1425 else:
1426 script.AssertSomeThumbprint(
1427 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1428 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1429
Doug Zongker2ea21062010-04-28 16:05:21 -07001430 metadata["pre-build"] = source_fp
1431 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001432
Doug Zongker55d93282011-01-25 17:03:34 -08001433 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001434 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1435 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001436 target_boot = common.GetBootableImage(
1437 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001438 updating_boot = (not OPTIONS.two_step and
1439 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001440
Doug Zongker55d93282011-01-25 17:03:34 -08001441 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001442 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1443 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001444 target_recovery = common.GetBootableImage(
1445 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001446 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001447
Doug Zongker881dd402009-09-20 14:03:55 -07001448 # Here's how we divide up the progress bar:
1449 # 0.1 for verifying the start state (PatchCheck calls)
1450 # 0.8 for applying patches (ApplyPatch calls)
1451 # 0.1 for unpacking verbatim files, symlinking, and doing the
1452 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001453
Michael Runge6e836112014-04-15 17:40:21 -07001454 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001455 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001456
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001457 # Two-step incremental package strategy (in chronological order,
1458 # which is *not* the order in which the generated script has
1459 # things):
1460 #
1461 # if stage is not "2/3" or "3/3":
1462 # do verification on current system
1463 # write recovery image to boot partition
1464 # set stage to "2/3"
1465 # reboot to boot partition and restart recovery
1466 # else if stage is "2/3":
1467 # write recovery image to recovery partition
1468 # set stage to "3/3"
1469 # reboot to recovery partition and restart recovery
1470 # else:
1471 # (stage must be "3/3")
1472 # perform update:
1473 # patch system files, etc.
1474 # force full install of new boot image
1475 # set up system to update recovery partition on first boot
Dan Albert8b72aef2015-03-23 19:13:21 -07001476 # complete script normally
1477 # (allow recovery to mark itself finished and reboot)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001478
1479 if OPTIONS.two_step:
Tao Baodd24da92015-07-29 14:09:23 -07001480 if not OPTIONS.source_info_dict.get("multistage_support", None):
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001481 assert False, "two-step packages not supported by this build"
Tao Baodd24da92015-07-29 14:09:23 -07001482 fs = OPTIONS.source_info_dict["fstab"]["/misc"]
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001483 assert fs.fs_type.upper() == "EMMC", \
1484 "two-step packages only supported on devices with EMMC /misc partitions"
1485 bcb_dev = {"bcb_dev": fs.device}
1486 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1487 script.AppendExtra("""
Michael Rungefb8886d2014-10-23 13:51:04 -07001488if get_stage("%(bcb_dev)s") == "2/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001489""" % bcb_dev)
Dan Albert8b72aef2015-03-23 19:13:21 -07001490 script.AppendExtra("sleep(20);\n")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001491 script.WriteRawImage("/recovery", "recovery.img")
1492 script.AppendExtra("""
1493set_stage("%(bcb_dev)s", "3/3");
1494reboot_now("%(bcb_dev)s", "recovery");
Michael Rungefb8886d2014-10-23 13:51:04 -07001495else if get_stage("%(bcb_dev)s") != "3/3" then
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001496""" % bcb_dev)
1497
Tao Bao6c55a8a2015-04-08 15:30:27 -07001498 # Dump fingerprints
1499 script.Print("Source: %s" % (source_fp,))
1500 script.Print("Target: %s" % (target_fp,))
1501
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001502 script.Print("Verifying current system...")
1503
Doug Zongkere5ff5902012-01-17 10:55:37 -08001504 device_specific.IncrementalOTA_VerifyBegin()
1505
Doug Zongker881dd402009-09-20 14:03:55 -07001506 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001507 so_far = system_diff.EmitVerification(script)
1508 if vendor_diff:
1509 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001510
Tao Baod8d14be2016-02-04 14:26:02 -08001511 size = []
1512 if system_diff.patch_list:
1513 size.append(system_diff.largest_source_size)
1514 if vendor_diff:
1515 if vendor_diff.patch_list:
1516 size.append(vendor_diff.largest_source_size)
1517
Doug Zongker5da317e2009-06-02 13:38:17 -07001518 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001519 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001520 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001521 print "boot target: %d source: %d diff: %d" % (
1522 target_boot.size, source_boot.size, len(d))
1523
Doug Zongker048e7ca2009-06-15 14:31:53 -07001524 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001525
Tao Baodd24da92015-07-29 14:09:23 -07001526 boot_type, boot_device = common.GetTypeAndDevice(
1527 "/boot", OPTIONS.source_info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001528
1529 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1530 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001531 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001532 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001533 so_far += source_boot.size
Tao Baod8d14be2016-02-04 14:26:02 -08001534 size.append(target_boot.size)
Doug Zongker5da317e2009-06-02 13:38:17 -07001535
Tao Baod8d14be2016-02-04 14:26:02 -08001536 if size:
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001537 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001538
Doug Zongker05d3dea2009-06-22 11:32:31 -07001539 device_specific.IncrementalOTA_VerifyEnd()
1540
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001541 if OPTIONS.two_step:
1542 script.WriteRawImage("/boot", "recovery.img")
1543 script.AppendExtra("""
1544set_stage("%(bcb_dev)s", "2/3");
1545reboot_now("%(bcb_dev)s", "");
1546else
1547""" % bcb_dev)
1548
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001549 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001550
Doug Zongkere5ff5902012-01-17 10:55:37 -08001551 device_specific.IncrementalOTA_InstallBegin()
1552
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001553 if OPTIONS.two_step:
1554 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1555 script.WriteRawImage("/boot", "boot.img")
1556 print "writing full boot image (forced by two-step mode)"
1557
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001558 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001559 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1560 if vendor_diff:
1561 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001562
Doug Zongker881dd402009-09-20 14:03:55 -07001563 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001564 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1565 if vendor_diff:
1566 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001567 if updating_boot:
1568 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001569
1570 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001571 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1572 if vendor_diff:
1573 script.Print("Patching vendor files...")
1574 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001575
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001576 if not OPTIONS.two_step:
1577 if updating_boot:
1578 # Produce the boot image by applying a patch to the current
1579 # contents of the boot partition, and write it back to the
1580 # partition.
1581 script.Print("Patching boot image...")
1582 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1583 % (boot_type, boot_device,
1584 source_boot.size, source_boot.sha1,
1585 target_boot.size, target_boot.sha1),
1586 "-",
1587 target_boot.size, target_boot.sha1,
1588 source_boot.sha1, "patch/boot.img.p")
1589 so_far += target_boot.size
1590 script.SetProgress(so_far / total_patch_size)
1591 print "boot image changed; including."
1592 else:
1593 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001594
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001595 system_items = ItemSet("system", "META/filesystem_config.txt")
1596 if vendor_diff:
1597 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1598
Doug Zongkereef39442009-04-02 12:14:19 -07001599 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001600 # Recovery is generated as a patch using both the boot image
1601 # (which contains the same linux kernel as recovery) and the file
1602 # /system/etc/recovery-resource.dat (which contains all the images
1603 # used in the recovery UI) as sources. This lets us minimize the
1604 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001605 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001606 # For older builds where recovery-resource.dat is not present, we
1607 # use only the boot image as the source.
1608
Doug Zongkerc9253822014-02-04 12:17:58 -08001609 if not target_has_recovery_patch:
1610 def output_sink(fn, data):
1611 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Dan Albert8b72aef2015-03-23 19:13:21 -07001612 system_items.Get("system/" + fn)
Doug Zongkerc9253822014-02-04 12:17:58 -08001613
1614 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1615 target_recovery, target_boot)
1616 script.DeleteFiles(["/system/recovery-from-boot.p",
Tao Baof2cffbd2015-07-22 12:33:18 -07001617 "/system/etc/recovery.img",
Doug Zongkerc9253822014-02-04 12:17:58 -08001618 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001619 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001620 else:
1621 print "recovery image unchanged; skipping."
1622
Doug Zongker881dd402009-09-20 14:03:55 -07001623 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001624
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001625 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1626 if vendor_diff:
1627 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1628
1629 temp_script = script.MakeTemporary()
1630 system_items.GetMetadata(target_zip)
1631 system_items.Get("system").SetPermissions(temp_script)
1632 if vendor_diff:
1633 vendor_items.GetMetadata(target_zip)
1634 vendor_items.Get("vendor").SetPermissions(temp_script)
1635
1636 # Note that this call will mess up the trees of Items, so make sure
1637 # we're done with them.
1638 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1639 if vendor_diff:
1640 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001641
1642 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001643 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1644
1645 # Delete all the symlinks in source that aren't in target. This
1646 # needs to happen before verbatim files are unpacked, in case a
1647 # symlink in the source is replaced by a real file in the target.
Tao Bao84006ea2015-09-02 10:28:08 -07001648
1649 # If a symlink in the source will be replaced by a regular file, we cannot
1650 # delete the symlink/file in case the package gets applied again. For such
1651 # a symlink, we prepend a sha1_check() to detect if it has been updated.
1652 # (Bug: 23646151)
1653 replaced_symlinks = dict()
1654 if system_diff:
1655 for i in system_diff.verbatim_targets:
1656 replaced_symlinks["/%s" % (i[0],)] = i[2]
1657 if vendor_diff:
1658 for i in vendor_diff.verbatim_targets:
1659 replaced_symlinks["/%s" % (i[0],)] = i[2]
1660
1661 if system_diff:
1662 for tf in system_diff.renames.values():
1663 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1664 if vendor_diff:
1665 for tf in vendor_diff.renames.values():
1666 replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
1667
1668 always_delete = []
1669 may_delete = []
Doug Zongkereef39442009-04-02 12:14:19 -07001670 for dest, link in source_symlinks:
1671 if link not in target_symlinks_d:
Tao Bao84006ea2015-09-02 10:28:08 -07001672 if link in replaced_symlinks:
1673 may_delete.append((link, replaced_symlinks[link]))
1674 else:
1675 always_delete.append(link)
1676 script.DeleteFiles(always_delete)
1677 script.DeleteFilesIfNotMatching(may_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001678
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001679 if system_diff.verbatim_targets:
1680 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001681 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001682 if vendor_diff and vendor_diff.verbatim_targets:
1683 script.Print("Unpacking new vendor files...")
1684 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001685
Doug Zongkerc9253822014-02-04 12:17:58 -08001686 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001687 script.Print("Unpacking new recovery...")
1688 script.UnpackPackageDir("recovery", "/system")
1689
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001690 system_diff.EmitRenames(script)
1691 if vendor_diff:
1692 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001693
Doug Zongker05d3dea2009-06-22 11:32:31 -07001694 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001695
1696 # Create all the symlinks that don't already exist, or point to
1697 # somewhere different than what we want. Delete each symlink before
1698 # creating it, since the 'symlink' command won't overwrite.
1699 to_create = []
1700 for dest, link in target_symlinks:
1701 if link in source_symlinks_d:
1702 if dest != source_symlinks_d[link]:
1703 to_create.append((dest, link))
1704 else:
1705 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001706 script.DeleteFiles([i[1] for i in to_create])
1707 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001708
1709 # Now that the symlinks are created, we can set all the
1710 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001711 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001712
Doug Zongker881dd402009-09-20 14:03:55 -07001713 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001714 device_specific.IncrementalOTA_InstallEnd()
1715
Doug Zongker1c390a22009-05-14 19:06:36 -07001716 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001717 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001718
Doug Zongkere92f15a2011-08-26 13:46:40 -07001719 # Patch the build.prop file last, so if something fails but the
1720 # device can still come up, it appears to be the old build and will
1721 # get set the OTA package again to retry.
1722 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001723 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001724
Doug Zongker922206e2014-03-04 13:16:24 -08001725 if OPTIONS.wipe_user_data:
1726 script.Print("Erasing user data...")
1727 script.FormatPartition("/data")
1728
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001729 if OPTIONS.two_step:
1730 script.AppendExtra("""
1731set_stage("%(bcb_dev)s", "");
1732endif;
1733endif;
1734""" % bcb_dev)
1735
Michael Runge63f01de2014-10-28 19:24:19 -07001736 if OPTIONS.verify and system_diff:
1737 script.Print("Remounting and verifying system partition files...")
1738 script.Unmount("/system")
Tao Bao269d7852015-12-02 15:49:13 -08001739 script.Mount("/system", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001740 system_diff.EmitExplicitTargetVerification(script)
1741
1742 if OPTIONS.verify and vendor_diff:
1743 script.Print("Remounting and verifying vendor partition files...")
1744 script.Unmount("/vendor")
Tao Bao269d7852015-12-02 15:49:13 -08001745 script.Mount("/vendor", recovery_mount_options)
Michael Runge63f01de2014-10-28 19:24:19 -07001746 vendor_diff.EmitExplicitTargetVerification(script)
Doug Zongker25568482014-03-03 10:21:27 -08001747 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Michael Runge63f01de2014-10-28 19:24:19 -07001748
Tao Baod8d14be2016-02-04 14:26:02 -08001749 metadata["ota-required-cache"] = str(script.required_cache)
Doug Zongker2ea21062010-04-28 16:05:21 -07001750 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001751
1752
1753def main(argv):
1754
1755 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001756 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001757 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001758 elif o in ("-k", "--package_key"):
1759 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001760 elif o in ("-i", "--incremental_from"):
1761 OPTIONS.incremental_source = a
Tao Bao43078aa2015-04-21 14:32:35 -07001762 elif o == "--full_radio":
1763 OPTIONS.full_radio = True
leozwangaa6c1a12015-08-14 10:57:58 -07001764 elif o == "--full_bootloader":
1765 OPTIONS.full_bootloader = True
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001766 elif o in ("-w", "--wipe_user_data"):
1767 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001768 elif o in ("-n", "--no_prereq"):
1769 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001770 elif o in ("-o", "--oem_settings"):
1771 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001772 elif o in ("-e", "--extra_script"):
1773 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001774 elif o in ("-a", "--aslr_mode"):
1775 if a in ("on", "On", "true", "True", "yes", "Yes"):
1776 OPTIONS.aslr_mode = True
1777 else:
1778 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001779 elif o in ("-t", "--worker_threads"):
1780 if a.isdigit():
1781 OPTIONS.worker_threads = int(a)
1782 else:
1783 raise ValueError("Cannot parse value %r for option %r - only "
1784 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001785 elif o in ("-2", "--two_step"):
1786 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001787 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001788 OPTIONS.no_signing = True
Dan Albert8b72aef2015-03-23 19:13:21 -07001789 elif o == "--verify":
Michael Runge63f01de2014-10-28 19:24:19 -07001790 OPTIONS.verify = True
Doug Zongker26e66192014-02-20 13:22:07 -08001791 elif o == "--block":
1792 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001793 elif o in ("-b", "--binary"):
1794 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001795 elif o in ("--no_fallback_to_full",):
1796 OPTIONS.fallback_to_full = False
Tao Bao8dcf7382015-05-21 14:09:49 -07001797 elif o == "--stash_threshold":
1798 try:
1799 OPTIONS.stash_threshold = float(a)
1800 except ValueError:
1801 raise ValueError("Cannot parse value %r for option %r - expecting "
1802 "a float" % (a, o))
Tao Bao9bc6bb22015-11-09 16:58:28 -08001803 elif o == "--gen_verify":
1804 OPTIONS.gen_verify = True
Tao Baod62c6032015-11-30 09:40:20 -08001805 elif o == "--log_diff":
1806 OPTIONS.log_diff = a
Doug Zongkereef39442009-04-02 12:14:19 -07001807 else:
1808 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001809 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001810
1811 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001812 extra_opts="b:k:i:d:wne:t:a:2o:",
Dan Albert8b72aef2015-03-23 19:13:21 -07001813 extra_long_opts=[
1814 "board_config=",
1815 "package_key=",
1816 "incremental_from=",
Tao Bao43078aa2015-04-21 14:32:35 -07001817 "full_radio",
leozwangaa6c1a12015-08-14 10:57:58 -07001818 "full_bootloader",
Dan Albert8b72aef2015-03-23 19:13:21 -07001819 "wipe_user_data",
1820 "no_prereq",
1821 "extra_script=",
1822 "worker_threads=",
1823 "aslr_mode=",
1824 "two_step",
1825 "no_signing",
1826 "block",
1827 "binary=",
1828 "oem_settings=",
1829 "verify",
1830 "no_fallback_to_full",
Tao Bao8dcf7382015-05-21 14:09:49 -07001831 "stash_threshold=",
Tao Baod62c6032015-11-30 09:40:20 -08001832 "gen_verify",
1833 "log_diff=",
Dan Albert8b72aef2015-03-23 19:13:21 -07001834 ], extra_option_handler=option_handler)
Doug Zongkereef39442009-04-02 12:14:19 -07001835
1836 if len(args) != 2:
1837 common.Usage(__doc__)
1838 sys.exit(1)
1839
Tao Baoc098e9e2016-01-07 13:03:56 -08001840 # Load the dict file from the zip directly to have a peek at the OTA type.
1841 # For packages using A/B update, unzipping is not needed.
1842 input_zip = zipfile.ZipFile(args[0], "r")
1843 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
1844 common.ZipClose(input_zip)
1845
1846 ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1847
1848 if ab_update:
1849 if OPTIONS.incremental_source is not None:
1850 OPTIONS.target_info_dict = OPTIONS.info_dict
1851 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
1852 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1853 common.ZipClose(source_zip)
1854
1855 if OPTIONS.verbose:
1856 print "--- target info ---"
1857 common.DumpInfoDict(OPTIONS.info_dict)
1858
1859 if OPTIONS.incremental_source is not None:
1860 print "--- source info ---"
1861 common.DumpInfoDict(OPTIONS.source_info_dict)
1862
1863 WriteABOTAPackageWithBrilloScript(
1864 target_file=args[0],
1865 output_file=args[1],
1866 source_file=OPTIONS.incremental_source)
1867
1868 print "done."
1869 return
1870
Doug Zongker1c390a22009-05-14 19:06:36 -07001871 if OPTIONS.extra_script is not None:
1872 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1873
Doug Zongkereef39442009-04-02 12:14:19 -07001874 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001875 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001876
Doug Zongkereef39442009-04-02 12:14:19 -07001877 OPTIONS.target_tmp = OPTIONS.input_tmp
Tao Bao2c15d9e2015-07-09 11:51:16 -07001878 OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
Kenny Roote2e9f612013-05-29 12:59:35 -07001879
Doug Zongker37974732010-09-16 17:44:38 -07001880 if OPTIONS.verbose:
1881 print "--- target info ---"
1882 common.DumpInfoDict(OPTIONS.info_dict)
1883
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001884 # If the caller explicitly specified the device-specific extensions
1885 # path via -s/--device_specific, use that. Otherwise, use
1886 # META/releasetools.py if it is present in the target target_files.
1887 # Otherwise, take the path of the file from 'tool_extensions' in the
1888 # info dict and look for that in the local filesystem, relative to
1889 # the current directory.
1890
Doug Zongker37974732010-09-16 17:44:38 -07001891 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001892 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1893 if os.path.exists(from_input):
1894 print "(using device-specific extensions from target_files)"
1895 OPTIONS.device_specific = from_input
1896 else:
1897 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1898
Doug Zongker37974732010-09-16 17:44:38 -07001899 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001900 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001901
Tao Baoc098e9e2016-01-07 13:03:56 -08001902 if OPTIONS.info_dict.get("no_recovery") == "true":
Tao Baodb45efa2015-10-27 19:25:18 -07001903 raise common.ExternalError(
1904 "--- target build has specified no recovery ---")
1905
Tao Bao767e3ac2015-11-10 12:19:19 -08001906 # Use the default key to sign the package if not specified with package_key.
1907 if not OPTIONS.no_signing:
1908 if OPTIONS.package_key is None:
1909 OPTIONS.package_key = OPTIONS.info_dict.get(
1910 "default_system_dev_certificate",
1911 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001912
Tao Bao767e3ac2015-11-10 12:19:19 -08001913 # Set up the output zip. Create a temporary zip file if signing is needed.
1914 if OPTIONS.no_signing:
1915 if os.path.exists(args[1]):
1916 os.unlink(args[1])
1917 output_zip = zipfile.ZipFile(args[1], "w",
1918 compression=zipfile.ZIP_DEFLATED)
1919 else:
1920 temp_zip_file = tempfile.NamedTemporaryFile()
1921 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1922 compression=zipfile.ZIP_DEFLATED)
Doug Zongker62d4f182014-08-04 16:06:43 -07001923
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -08001924 # Non A/B OTAs rely on /cache partition to store temporary files.
Tao Bao767e3ac2015-11-10 12:19:19 -08001925 cache_size = OPTIONS.info_dict.get("cache_size", None)
Tao Baoc098e9e2016-01-07 13:03:56 -08001926 if cache_size is None:
Tao Bao767e3ac2015-11-10 12:19:19 -08001927 print "--- can't determine the cache partition size ---"
1928 OPTIONS.cache_size = cache_size
Tao Bao8dcf7382015-05-21 14:09:49 -07001929
Tao Bao9bc6bb22015-11-09 16:58:28 -08001930 # Generate a verify package.
1931 if OPTIONS.gen_verify:
1932 WriteVerifyPackage(input_zip, output_zip)
1933
Tao Bao767e3ac2015-11-10 12:19:19 -08001934 # Generate a full OTA.
Tao Bao9bc6bb22015-11-09 16:58:28 -08001935 elif OPTIONS.incremental_source is None:
Tao Baoc098e9e2016-01-07 13:03:56 -08001936 WriteFullOTAPackage(input_zip, output_zip)
Tao Bao767e3ac2015-11-10 12:19:19 -08001937
1938 # Generate an incremental OTA. It will fall back to generate a full OTA on
1939 # failure unless no_fallback_to_full is specified.
1940 else:
1941 print "unzipping source target-files..."
1942 OPTIONS.source_tmp, source_zip = common.UnzipTemp(
1943 OPTIONS.incremental_source)
1944 OPTIONS.target_info_dict = OPTIONS.info_dict
1945 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
1946 OPTIONS.source_tmp)
1947 if OPTIONS.verbose:
1948 print "--- source info ---"
1949 common.DumpInfoDict(OPTIONS.source_info_dict)
1950 try:
1951 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Tao Baod62c6032015-11-30 09:40:20 -08001952 if OPTIONS.log_diff:
1953 out_file = open(OPTIONS.log_diff, 'w')
1954 import target_files_diff
1955 target_files_diff.recursiveDiff('',
1956 OPTIONS.source_tmp,
1957 OPTIONS.input_tmp,
1958 out_file)
1959 out_file.close()
Tao Bao767e3ac2015-11-10 12:19:19 -08001960 except ValueError:
1961 if not OPTIONS.fallback_to_full:
1962 raise
1963 print "--- failed to build incremental; falling back to full ---"
1964 OPTIONS.incremental_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -07001965 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongker62d4f182014-08-04 16:06:43 -07001966
Tao Bao767e3ac2015-11-10 12:19:19 -08001967 common.ZipClose(output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001968
Tao Bao767e3ac2015-11-10 12:19:19 -08001969 # Sign the generated zip package unless no_signing is specified.
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001970 if not OPTIONS.no_signing:
1971 SignOutput(temp_zip_file.name, args[1])
1972 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001973
Doug Zongkereef39442009-04-02 12:14:19 -07001974 print "done."
1975
1976
1977if __name__ == '__main__':
1978 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001979 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001980 main(sys.argv[1:])
Dan Albert8b72aef2015-03-23 19:13:21 -07001981 except common.ExternalError as e:
Doug Zongkereef39442009-04-02 12:14:19 -07001982 print
1983 print " ERROR: %s" % (e,)
1984 print
1985 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001986 finally:
1987 common.Cleanup()