blob: 1afc6402022a21188e9c814bcc2baedb0e27383e [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
24 -b (--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
Doug Zongkerdbfaae52009-04-21 17:12:54 -070040 -w (--wipe_user_data)
41 Generate an OTA package that will wipe the user data partition
42 when installed.
43
Doug Zongker962069c2009-04-23 11:41:58 -070044 -n (--no_prereq)
45 Omit the timestamp prereq check normally included at the top of
46 the build scripts (used for developer OTA packages which
47 legitimately need to go back and forth).
48
Doug Zongker1c390a22009-05-14 19:06:36 -070049 -e (--extra_script) <file>
50 Insert the contents of file at the end of the update script.
51
Hristo Bojinovdafb0422010-08-26 14:35:16 -070052 -a (--aslr_mode) <on|off>
53 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050054
Doug Zongker9b23f2c2013-11-25 14:44:12 -080055 -2 (--two_step)
56 Generate a 'two-step' OTA package, where recovery is updated
57 first, so that any changes made to the system partition are done
58 using the new recovery (new kernel, etc.).
59
Doug Zongker26e66192014-02-20 13:22:07 -080060 --block
61 Generate a block-based OTA if possible. Will fall back to a
62 file-based OTA if the target_files is older and doesn't support
63 block-based OTAs.
64
Doug Zongkereef39442009-04-02 12:14:19 -070065"""
66
67import sys
68
Doug Zongkercf6d5a92014-02-18 10:57:07 -080069if sys.hexversion < 0x02070000:
70 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -070071 sys.exit(1)
72
73import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070074import errno
Doug Zongkereef39442009-04-02 12:14:19 -070075import os
76import re
Doug Zongkereef39442009-04-02 12:14:19 -070077import subprocess
78import tempfile
79import time
80import zipfile
81
davidcad0bb92011-03-15 14:21:38 +000082try:
83 from hashlib import sha1 as sha1
84except ImportError:
85 from sha import sha as sha1
86
Doug Zongkereef39442009-04-02 12:14:19 -070087import common
Doug Zongker01ce19c2014-02-04 13:48:15 -080088import img_from_target_files
Doug Zongkerc494d7c2009-06-18 08:43:44 -070089import edify_generator
Geremy Condra36bd3652014-02-06 19:45:10 -080090import build_image
Doug Zongkereef39442009-04-02 12:14:19 -070091
92OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -070093OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -070094OPTIONS.incremental_source = None
95OPTIONS.require_verbatim = set()
96OPTIONS.prohibit_verbatim = set(("system/build.prop",))
97OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070098OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070099OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700100OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700101OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -0700102OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800103OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900104OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800105OPTIONS.block_based = False
Doug Zongkereef39442009-04-02 12:14:19 -0700106
107def MostPopularKey(d, default):
108 """Given a dict, return the key corresponding to the largest
109 value. Returns 'default' if the dict is empty."""
110 x = [(v, k) for (k, v) in d.iteritems()]
111 if not x: return default
112 x.sort()
113 return x[-1][1]
114
115
116def IsSymlink(info):
117 """Return true if the zipfile.ZipInfo object passed in represents a
118 symlink."""
119 return (info.external_attr >> 16) == 0120777
120
Hristo Bojinov96be7202010-08-02 10:26:17 -0700121def IsRegular(info):
122 """Return true if the zipfile.ZipInfo object passed in represents a
123 symlink."""
124 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700125
Michael Runge4038aa82013-12-13 18:06:28 -0800126def ClosestFileMatch(src, tgtfiles, existing):
127 """Returns the closest file match between a source file and list
128 of potential matches. The exact filename match is preferred,
129 then the sha1 is searched for, and finally a file with the same
130 basename is evaluated. Rename support in the updater-binary is
131 required for the latter checks to be used."""
132
133 result = tgtfiles.get("path:" + src.name)
134 if result is not None:
135 return result
136
137 if not OPTIONS.target_info_dict.get("update_rename_support", False):
138 return None
139
140 if src.size < 1000:
141 return None
142
143 result = tgtfiles.get("sha1:" + src.sha1)
144 if result is not None and existing.get(result.name) is None:
145 return result
146 result = tgtfiles.get("file:" + src.name.split("/")[-1])
147 if result is not None and existing.get(result.name) is None:
148 return result
149 return None
150
Doug Zongkereef39442009-04-02 12:14:19 -0700151class Item:
152 """Items represent the metadata (user, group, mode) of files and
153 directories in the system image."""
154 ITEMS = {}
155 def __init__(self, name, dir=False):
156 self.name = name
157 self.uid = None
158 self.gid = None
159 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700160 self.selabel = None
161 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700162 self.dir = dir
163
164 if name:
165 self.parent = Item.Get(os.path.dirname(name), dir=True)
166 self.parent.children.append(self)
167 else:
168 self.parent = None
169 if dir:
170 self.children = []
171
172 def Dump(self, indent=0):
173 if self.uid is not None:
174 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
175 else:
176 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
177 if self.dir:
178 print "%s%s" % (" "*indent, self.descendants)
179 print "%s%s" % (" "*indent, self.best_subtree)
180 for i in self.children:
181 i.Dump(indent=indent+1)
182
183 @classmethod
184 def Get(cls, name, dir=False):
185 if name not in cls.ITEMS:
186 cls.ITEMS[name] = Item(name, dir=dir)
187 return cls.ITEMS[name]
188
189 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700190 def GetMetadata(cls, input_zip):
191
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700192 # The target_files contains a record of what the uid,
193 # gid, and mode are supposed to be.
194 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700195
196 for line in output.split("\n"):
197 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700198 columns = line.split()
199 name, uid, gid, mode = columns[:4]
200 selabel = None
201 capabilities = None
202
203 # After the first 4 columns, there are a series of key=value
204 # pairs. Extract out the fields we care about.
205 for element in columns[4:]:
206 key, value = element.split("=")
207 if key == "selabel":
208 selabel = value
209 if key == "capabilities":
210 capabilities = value
211
Doug Zongker283e2a12010-03-15 17:52:32 -0700212 i = cls.ITEMS.get(name, None)
213 if i is not None:
214 i.uid = int(uid)
215 i.gid = int(gid)
216 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700217 i.selabel = selabel
218 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700219 if i.dir:
220 i.children.sort(key=lambda i: i.name)
221
222 # set metadata for the files generated by this script.
223 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700224 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700225 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700226 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700227
228 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700229 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
230 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700231 set_perm to correctly chown/chmod all the files to their desired
232 values. Recursively calls itself for all descendants.
233
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700234 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700235 all descendants of this node. (dmode or fmode may be None.) Also
236 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700237 dmode, fmode, selabel, capabilities) tuple that will match the most
238 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700239 """
240
241 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700242 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700243 for i in self.children:
244 if i.dir:
245 for k, v in i.CountChildMetadata().iteritems():
246 d[k] = d.get(k, 0) + v
247 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700248 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700249 d[k] = d.get(k, 0) + 1
250
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700251 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
252 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700253
254 # First, find the (uid, gid) pair that matches the most
255 # descendants.
256 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700258 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
259 ug = MostPopularKey(ug, (0, 0))
260
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700261 # Now find the dmode, fmode, selabel, and capabilities that match
262 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700263 best_dmode = (0, 0755)
264 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700265 best_selabel = (0, None)
266 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700267 for k, count in d.iteritems():
268 if k[:2] != ug: continue
269 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
270 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700271 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
272 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
273 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700274
275 return d
276
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700277 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700278 """Append set_perm/set_perm_recursive commands to 'script' to
279 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700280 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700281
282 self.CountChildMetadata()
283
284 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700285 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700286 # item (and all its children) have already been set to. We only
287 # need to issue set_perm/set_perm_recursive commands if we're
288 # supposed to be something different.
289 if item.dir:
290 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700291 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700292 current = item.best_subtree
293
294 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700295 item.mode != current[2] or item.selabel != current[4] or \
296 item.capabilities != current[5]:
297 script.SetPermissions("/"+item.name, item.uid, item.gid,
298 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700299
300 for i in item.children:
301 recurse(i, current)
302 else:
303 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700304 item.mode != current[3] or item.selabel != current[4] or \
305 item.capabilities != current[5]:
306 script.SetPermissions("/"+item.name, item.uid, item.gid,
307 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700308
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700309 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700310
311
312def CopySystemFiles(input_zip, output_zip=None,
313 substitute=None):
314 """Copies files underneath system/ in the input zip to the output
315 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800316 list of symlinks. output_zip may be None, in which case the copy is
317 skipped (but the other side effects still happen). substitute is an
318 optional dict of {output filename: contents} to be output instead of
319 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700320 """
321
322 symlinks = []
323
324 for info in input_zip.infolist():
325 if info.filename.startswith("SYSTEM/"):
326 basefilename = info.filename[7:]
327 if IsSymlink(info):
328 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700329 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700330 else:
331 info2 = copy.copy(info)
332 fn = info2.filename = "system/" + basefilename
333 if substitute and fn in substitute and substitute[fn] is None:
334 continue
335 if output_zip is not None:
336 if substitute and fn in substitute:
337 data = substitute[fn]
338 else:
339 data = input_zip.read(info.filename)
340 output_zip.writestr(info2, data)
341 if fn.endswith("/"):
342 Item.Get(fn[:-1], dir=True)
343 else:
344 Item.Get(fn, dir=False)
345
346 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800347 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700348
349
Doug Zongkereef39442009-04-02 12:14:19 -0700350def SignOutput(temp_zip_name, output_zip_name):
351 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
352 pw = key_passwords[OPTIONS.package_key]
353
Doug Zongker951495f2009-08-14 12:44:19 -0700354 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
355 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700356
357
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700358def AppendAssertions(script, info_dict):
359 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700360 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700361
Doug Zongkereef39442009-04-02 12:14:19 -0700362
Doug Zongkerc9253822014-02-04 12:17:58 -0800363def HasRecoveryPatch(target_files_zip):
364 try:
365 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
366 return True
367 except KeyError:
368 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700369
370
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700371def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700372 # TODO: how to determine this? We don't know what version it will
373 # be installed on top of. For now, we expect the API just won't
374 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700375 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700376
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700377 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
378 OPTIONS.info_dict),
379 "pre-device": GetBuildProp("ro.product.device",
380 OPTIONS.info_dict),
381 "post-timestamp": GetBuildProp("ro.build.date.utc",
382 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700383 }
384
Doug Zongker05d3dea2009-06-22 11:32:31 -0700385 device_specific = common.DeviceSpecificParams(
386 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700387 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700388 output_zip=output_zip,
389 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700390 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700391 metadata=metadata,
392 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700393
Doug Zongkerc9253822014-02-04 12:17:58 -0800394 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800395 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800396
Doug Zongker962069c2009-04-23 11:41:58 -0700397 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700398 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700399 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
400 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700401
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700402 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700403 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800404
405 # Two-step package strategy (in chronological order, which is *not*
406 # the order in which the generated script has things):
407 #
408 # if stage is not "2/3" or "3/3":
409 # write recovery image to boot partition
410 # set stage to "2/3"
411 # reboot to boot partition and restart recovery
412 # else if stage is "2/3":
413 # write recovery image to recovery partition
414 # set stage to "3/3"
415 # reboot to recovery partition and restart recovery
416 # else:
417 # (stage must be "3/3")
418 # set stage to ""
419 # do normal full package installation:
420 # wipe and install system, boot image, etc.
421 # set up system to update recovery partition on first boot
422 # complete script normally (allow recovery to mark itself finished and reboot)
423
424 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
425 OPTIONS.input_tmp, "RECOVERY")
426 if OPTIONS.two_step:
427 if not OPTIONS.info_dict.get("multistage_support", None):
428 assert False, "two-step packages not supported by this build"
429 fs = OPTIONS.info_dict["fstab"]["/misc"]
430 assert fs.fs_type.upper() == "EMMC", \
431 "two-step packages only supported on devices with EMMC /misc partitions"
432 bcb_dev = {"bcb_dev": fs.device}
433 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
434 script.AppendExtra("""
435if get_stage("%(bcb_dev)s", "stage") == "2/3" then
436""" % bcb_dev)
437 script.WriteRawImage("/recovery", "recovery.img")
438 script.AppendExtra("""
439set_stage("%(bcb_dev)s", "3/3");
440reboot_now("%(bcb_dev)s", "recovery");
441else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
442""" % bcb_dev)
443
Doug Zongkere5ff5902012-01-17 10:55:37 -0800444 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700445
Doug Zongker01ce19c2014-02-04 13:48:15 -0800446 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700447
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700448 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800449 system_progress -= 0.1
450 script.ShowProgress(0.1, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700451 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700452
Kenny Rootf32dc712012-04-08 10:42:34 -0700453 if "selinux_fc" in OPTIONS.info_dict:
454 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500455
Doug Zongker01ce19c2014-02-04 13:48:15 -0800456 script.ShowProgress(system_progress, 30)
Doug Zongker26e66192014-02-20 13:22:07 -0800457 if block_based:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800458 img_from_target_files.AddSystem(output_zip, sparse=False)
459 script.WriteRawImage("/system", "system.img")
460 else:
461 script.FormatPartition("/system")
462 script.Mount("/system")
463 if not has_recovery_patch:
464 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800465 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700466
Doug Zongker01ce19c2014-02-04 13:48:15 -0800467 symlinks = CopySystemFiles(input_zip, output_zip)
468 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700469
Doug Zongker55d93282011-01-25 17:03:34 -0800470 boot_img = common.GetBootableImage("boot.img", "boot.img",
471 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800472
473 if not has_recovery_patch:
474 def output_sink(fn, data):
475 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
476 Item.Get("system/" + fn, dir=False)
477
478 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
479 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700480
Doug Zongker01ce19c2014-02-04 13:48:15 -0800481 Item.GetMetadata(input_zip)
482 Item.Get("system").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700483
Doug Zongker37974732010-09-16 17:44:38 -0700484 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700485 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700486
Doug Zongker01ce19c2014-02-04 13:48:15 -0800487 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700488 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700489
Doug Zongker01ce19c2014-02-04 13:48:15 -0800490 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700491 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700492
Doug Zongker1c390a22009-05-14 19:06:36 -0700493 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700494 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700495
Doug Zongker14833602010-02-02 13:12:04 -0800496 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800497
498 if OPTIONS.two_step:
499 script.AppendExtra("""
500set_stage("%(bcb_dev)s", "");
501""" % bcb_dev)
502 script.AppendExtra("else\n")
503 script.WriteRawImage("/boot", "recovery.img")
504 script.AppendExtra("""
505set_stage("%(bcb_dev)s", "2/3");
506reboot_now("%(bcb_dev)s", "");
507endif;
508endif;
509""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700510 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700511 WriteMetadata(metadata, output_zip)
512
Stephen Smalley56882bf2012-02-09 13:36:21 -0500513def WritePolicyConfig(file_context, output_zip):
514 f = open(file_context, 'r');
515 basename = os.path.basename(file_context)
516 common.ZipWriteStr(output_zip, basename, f.read())
517
Doug Zongker2ea21062010-04-28 16:05:21 -0700518
519def WriteMetadata(metadata, output_zip):
520 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
521 "".join(["%s=%s\n" % kv
522 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700523
Doug Zongkereef39442009-04-02 12:14:19 -0700524def LoadSystemFiles(z):
525 """Load all the files from SYSTEM/... in a given target-files
526 ZipFile, and return a dict of {filename: File object}."""
527 out = {}
528 for info in z.infolist():
529 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700530 basefilename = info.filename[7:]
531 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700532 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700533 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800534 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700535
536
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700537def GetBuildProp(prop, info_dict):
538 """Return the fingerprint of the build of a given target-files info_dict."""
539 try:
540 return info_dict.get("build.prop", {})[prop]
541 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700542 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700543
Michael Runge4038aa82013-12-13 18:06:28 -0800544def AddToKnownPaths(filename, known_paths):
545 if filename[-1] == "/":
546 return
547 dirs = filename.split("/")[:-1]
548 while len(dirs) > 0:
549 path = "/".join(dirs)
550 if path in known_paths:
551 break;
552 known_paths.add(path)
553 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700554
Geremy Condra36bd3652014-02-06 19:45:10 -0800555def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
556 source_version = OPTIONS.source_info_dict["recovery_api_version"]
557 target_version = OPTIONS.target_info_dict["recovery_api_version"]
558
559 if source_version == 0:
560 print ("WARNING: generating edify script for a source that "
561 "can't install it.")
562 script = edify_generator.EdifyGenerator(source_version,
563 OPTIONS.target_info_dict)
564
565 metadata = {"pre-device": GetBuildProp("ro.product.device",
566 OPTIONS.source_info_dict),
567 "post-timestamp": GetBuildProp("ro.build.date.utc",
568 OPTIONS.target_info_dict),
569 }
570
571 device_specific = common.DeviceSpecificParams(
572 source_zip=source_zip,
573 source_version=source_version,
574 target_zip=target_zip,
575 target_version=target_version,
576 output_zip=output_zip,
577 script=script,
578 metadata=metadata,
579 info_dict=OPTIONS.info_dict)
580
581 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
582 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
583 metadata["pre-build"] = source_fp
584 metadata["post-build"] = target_fp
585
586 source_boot = common.GetBootableImage(
587 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
588 OPTIONS.source_info_dict)
589 target_boot = common.GetBootableImage(
590 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
591 updating_boot = (not OPTIONS.two_step and
592 (source_boot.data != target_boot.data))
593
594 source_recovery = common.GetBootableImage(
595 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
596 OPTIONS.source_info_dict)
597 target_recovery = common.GetBootableImage(
598 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
599 updating_recovery = (source_recovery.data != target_recovery.data)
600
601 with tempfile.NamedTemporaryFile() as src_file:
602 with tempfile.NamedTemporaryFile() as tgt_file:
603 print "building source system image..."
604 src_file = tempfile.NamedTemporaryFile()
605 src_data = img_from_target_files.BuildSystem(
606 OPTIONS.source_tmp, OPTIONS.source_info_dict, sparse=False)
607 src_sys_sha1 = sha1(src_data).hexdigest()
608 print "source system sha1:", src_sys_sha1
609 src_file.write(src_data)
610 src_data = None
611
612 print "building target system image..."
613 tgt_file = tempfile.NamedTemporaryFile()
614 tgt_data = img_from_target_files.BuildSystem(
615 OPTIONS.target_tmp, OPTIONS.target_info_dict, sparse=False)
616 tgt_sys_sha1 = sha1(tgt_data).hexdigest()
617 print "target system sha1:", tgt_sys_sha1
618 tgt_sys_len = len(tgt_data)
619 tgt_file.write(tgt_data)
620 tgt_data = None
621
622 system_type, system_device = common.GetTypeAndDevice("/system", OPTIONS.info_dict)
623 system_patch = common.MakeSystemPatch(src_file, tgt_file)
624 system_patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
625
626 AppendAssertions(script, OPTIONS.target_info_dict)
627 device_specific.IncrementalOTA_Assertions()
628
629 # Two-step incremental package strategy (in chronological order,
630 # which is *not* the order in which the generated script has
631 # things):
632 #
633 # if stage is not "2/3" or "3/3":
634 # do verification on current system
635 # write recovery image to boot partition
636 # set stage to "2/3"
637 # reboot to boot partition and restart recovery
638 # else if stage is "2/3":
639 # write recovery image to recovery partition
640 # set stage to "3/3"
641 # reboot to recovery partition and restart recovery
642 # else:
643 # (stage must be "3/3")
644 # perform update:
645 # patch system files, etc.
646 # force full install of new boot image
647 # set up system to update recovery partition on first boot
648 # complete script normally (allow recovery to mark itself finished and reboot)
649
650 if OPTIONS.two_step:
651 if not OPTIONS.info_dict.get("multistage_support", None):
652 assert False, "two-step packages not supported by this build"
653 fs = OPTIONS.info_dict["fstab"]["/misc"]
654 assert fs.fs_type.upper() == "EMMC", \
655 "two-step packages only supported on devices with EMMC /misc partitions"
656 bcb_dev = {"bcb_dev": fs.device}
657 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
658 script.AppendExtra("""
659if get_stage("%(bcb_dev)s", "stage") == "2/3" then
660""" % bcb_dev)
661 script.AppendExtra("sleep(20);\n");
662 script.WriteRawImage("/recovery", "recovery.img")
663 script.AppendExtra("""
664set_stage("%(bcb_dev)s", "3/3");
665reboot_now("%(bcb_dev)s", "recovery");
666else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
667""" % bcb_dev)
668
669 script.Print("Verifying current system...")
670
671 device_specific.IncrementalOTA_VerifyBegin()
672
673 script.AssertRecoveryFingerprint(source_fp, target_fp)
674
675 if updating_boot:
Geremy Condra36bd3652014-02-06 19:45:10 -0800676 d = common.Difference(target_boot, source_boot)
677 _, _, d = d.ComputePatch()
678 print "boot target: %d source: %d diff: %d" % (
679 target_boot.size, source_boot.size, len(d))
680
681 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
682
683 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
684
685 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
686 (boot_type, boot_device,
687 source_boot.size, source_boot.sha1,
688 target_boot.size, target_boot.sha1))
689
690 device_specific.IncrementalOTA_VerifyEnd()
691
692 if OPTIONS.two_step:
693 script.WriteRawImage("/boot", "recovery.img")
694 script.AppendExtra("""
695set_stage("%(bcb_dev)s", "2/3");
696reboot_now("%(bcb_dev)s", "");
697else
698""" % bcb_dev)
699
700 script.Comment("---- start making changes here ----")
701
702 device_specific.IncrementalOTA_InstallBegin()
703
704 if OPTIONS.wipe_user_data:
705 script.Print("Erasing user data...")
706 script.FormatPartition("/data")
707
708 script.Print("Patching system image...")
709 script.Syspatch(system_device,
710 OPTIONS.info_dict["system_size"],
711 tgt_sys_sha1,
712 src_sys_sha1,
713 system_patch.name)
714
715 if OPTIONS.two_step:
716 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
717 script.WriteRawImage("/boot", "boot.img")
718 print "writing full boot image (forced by two-step mode)"
719
720 if not OPTIONS.two_step:
721 if updating_boot:
722 # Produce the boot image by applying a patch to the current
723 # contents of the boot partition, and write it back to the
724 # partition.
725 script.Print("Patching boot image...")
726 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
727 % (boot_type, boot_device,
728 source_boot.size, source_boot.sha1,
729 target_boot.size, target_boot.sha1),
730 "-",
731 target_boot.size, target_boot.sha1,
732 source_boot.sha1, "patch/boot.img.p")
733 print "boot image changed; including."
734 else:
735 print "boot image unchanged; skipping."
736
737 # Do device-specific installation (eg, write radio image).
738 device_specific.IncrementalOTA_InstallEnd()
739
740 if OPTIONS.extra_script is not None:
741 script.AppendExtra(OPTIONS.extra_script)
742
743 if OPTIONS.two_step:
744 script.AppendExtra("""
745set_stage("%(bcb_dev)s", "");
746endif;
747endif;
748""" % bcb_dev)
749
750 script.SetProgress(1)
751 script.AddToZip(target_zip, output_zip)
752 WriteMetadata(metadata, output_zip)
753
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700754def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -0800755 target_has_recovery_patch = HasRecoveryPatch(target_zip)
756 source_has_recovery_patch = HasRecoveryPatch(source_zip)
757
Doug Zongker26e66192014-02-20 13:22:07 -0800758 if (OPTIONS.block_based and
759 target_has_recovery_patch and
760 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -0800761 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
762
Doug Zongker37974732010-09-16 17:44:38 -0700763 source_version = OPTIONS.source_info_dict["recovery_api_version"]
764 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700765
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700766 if source_version == 0:
767 print ("WARNING: generating edify script for a source that "
768 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700769 script = edify_generator.EdifyGenerator(source_version,
770 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700771
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700772 metadata = {"pre-device": GetBuildProp("ro.product.device",
773 OPTIONS.source_info_dict),
774 "post-timestamp": GetBuildProp("ro.build.date.utc",
775 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700776 }
777
Doug Zongker05d3dea2009-06-22 11:32:31 -0700778 device_specific = common.DeviceSpecificParams(
779 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800780 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700781 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800782 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700783 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700784 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700785 metadata=metadata,
786 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700787
Doug Zongkereef39442009-04-02 12:14:19 -0700788 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800789 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700790 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800791 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700792
793 verbatim_targets = []
794 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700795 diffs = []
Michael Runge4038aa82013-12-13 18:06:28 -0800796 renames = {}
797 known_paths = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700798 largest_source_size = 0
Michael Runge4038aa82013-12-13 18:06:28 -0800799
800 matching_file_cache = {}
801 for fn, sf in source_data.items():
802 assert fn == sf.name
803 matching_file_cache["path:" + fn] = sf
804 if fn in target_data.keys():
805 AddToKnownPaths(fn, known_paths)
806 # Only allow eligibility for filename/sha matching
807 # if there isn't a perfect path match.
808 if target_data.get(sf.name) is None:
809 matching_file_cache["file:" + fn.split("/")[-1]] = sf
810 matching_file_cache["sha:" + sf.sha1] = sf
811
Doug Zongkereef39442009-04-02 12:14:19 -0700812 for fn in sorted(target_data.keys()):
813 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700814 assert fn == tf.name
Michael Runge4038aa82013-12-13 18:06:28 -0800815 sf = ClosestFileMatch(tf, matching_file_cache, renames)
816 if sf is not None and sf.name != tf.name:
817 print "File has moved from " + sf.name + " to " + tf.name
818 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700819
820 if sf is None or fn in OPTIONS.require_verbatim:
821 # This file should be included verbatim
822 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700823 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700824 print "send", fn, "verbatim"
825 tf.AddToZip(output_zip)
826 verbatim_targets.append((fn, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800827 if fn in target_data.keys():
828 AddToKnownPaths(fn, known_paths)
Doug Zongkereef39442009-04-02 12:14:19 -0700829 elif tf.sha1 != sf.sha1:
830 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700831 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700832 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800833 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700834 pass
835
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700836 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700837
838 for diff in diffs:
839 tf, sf, d = diff.GetPatch()
Michael Runge4038aa82013-12-13 18:06:28 -0800840 path = "/".join(tf.name.split("/")[:-1])
841 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
842 path not in known_paths:
Doug Zongker761e6422009-09-25 10:45:39 -0700843 # patch is almost as big as the file; don't bother patching
Michael Runge4038aa82013-12-13 18:06:28 -0800844 # or a patch + rename cannot take place due to the target
845 # directory not existing
Doug Zongker761e6422009-09-25 10:45:39 -0700846 tf.AddToZip(output_zip)
847 verbatim_targets.append((tf.name, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800848 if sf.name in renames:
849 del renames[sf.name]
850 AddToKnownPaths(tf.name, known_paths)
Doug Zongker761e6422009-09-25 10:45:39 -0700851 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800852 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
853 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700854 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700855
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700856 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
857 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700858 metadata["pre-build"] = source_fp
859 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700860
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700861 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700862 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700863
Doug Zongker55d93282011-01-25 17:03:34 -0800864 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700865 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
866 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800867 target_boot = common.GetBootableImage(
868 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800869 updating_boot = (not OPTIONS.two_step and
870 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700871
Doug Zongker55d93282011-01-25 17:03:34 -0800872 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700873 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
874 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800875 target_recovery = common.GetBootableImage(
876 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700877 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700878
Doug Zongker881dd402009-09-20 14:03:55 -0700879 # Here's how we divide up the progress bar:
880 # 0.1 for verifying the start state (PatchCheck calls)
881 # 0.8 for applying patches (ApplyPatch calls)
882 # 0.1 for unpacking verbatim files, symlinking, and doing the
883 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700884
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700885 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700886 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700887
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800888 # Two-step incremental package strategy (in chronological order,
889 # which is *not* the order in which the generated script has
890 # things):
891 #
892 # if stage is not "2/3" or "3/3":
893 # do verification on current system
894 # write recovery image to boot partition
895 # set stage to "2/3"
896 # reboot to boot partition and restart recovery
897 # else if stage is "2/3":
898 # write recovery image to recovery partition
899 # set stage to "3/3"
900 # reboot to recovery partition and restart recovery
901 # else:
902 # (stage must be "3/3")
903 # perform update:
904 # patch system files, etc.
905 # force full install of new boot image
906 # set up system to update recovery partition on first boot
907 # complete script normally (allow recovery to mark itself finished and reboot)
908
909 if OPTIONS.two_step:
910 if not OPTIONS.info_dict.get("multistage_support", None):
911 assert False, "two-step packages not supported by this build"
912 fs = OPTIONS.info_dict["fstab"]["/misc"]
913 assert fs.fs_type.upper() == "EMMC", \
914 "two-step packages only supported on devices with EMMC /misc partitions"
915 bcb_dev = {"bcb_dev": fs.device}
916 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
917 script.AppendExtra("""
918if get_stage("%(bcb_dev)s", "stage") == "2/3" then
919""" % bcb_dev)
920 script.AppendExtra("sleep(20);\n");
921 script.WriteRawImage("/recovery", "recovery.img")
922 script.AppendExtra("""
923set_stage("%(bcb_dev)s", "3/3");
924reboot_now("%(bcb_dev)s", "recovery");
925else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
926""" % bcb_dev)
927
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700928 script.Print("Verifying current system...")
929
Doug Zongkere5ff5902012-01-17 10:55:37 -0800930 device_specific.IncrementalOTA_VerifyBegin()
931
Doug Zongker881dd402009-09-20 14:03:55 -0700932 script.ShowProgress(0.1, 0)
Doug Zongker881dd402009-09-20 14:03:55 -0700933 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700934
Michael Runge4038aa82013-12-13 18:06:28 -0800935 for tf, sf, size, patch_sha in patch_list:
936 if tf.name != sf.name:
937 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
938 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700939 so_far += sf.size
Doug Zongkereef39442009-04-02 12:14:19 -0700940
Doug Zongker5da317e2009-06-02 13:38:17 -0700941 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700942 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700943 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700944 print "boot target: %d source: %d diff: %d" % (
945 target_boot.size, source_boot.size, len(d))
946
Doug Zongker048e7ca2009-06-15 14:31:53 -0700947 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700948
Doug Zongker96a57e72010-09-26 14:57:41 -0700949 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700950
951 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
952 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700953 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700954 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700955 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -0700956
957 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700958 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800959
Doug Zongker05d3dea2009-06-22 11:32:31 -0700960 device_specific.IncrementalOTA_VerifyEnd()
961
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800962 if OPTIONS.two_step:
963 script.WriteRawImage("/boot", "recovery.img")
964 script.AppendExtra("""
965set_stage("%(bcb_dev)s", "2/3");
966reboot_now("%(bcb_dev)s", "");
967else
968""" % bcb_dev)
969
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700970 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700971
Doug Zongkere5ff5902012-01-17 10:55:37 -0800972 device_specific.IncrementalOTA_InstallBegin()
973
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800974 if OPTIONS.two_step:
975 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
976 script.WriteRawImage("/boot", "boot.img")
977 print "writing full boot image (forced by two-step mode)"
978
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700979 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700980 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700981 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700982
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700983 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700984 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
985 ["/"+i for i in sorted(source_data)
Michael Runge4038aa82013-12-13 18:06:28 -0800986 if i not in target_data and
987 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700988 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700989
Doug Zongker881dd402009-09-20 14:03:55 -0700990 script.ShowProgress(0.8, 0)
991 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
992 if updating_boot:
993 total_patch_size += target_boot.size
994 so_far = 0
995
996 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700997 deferred_patch_list = []
998 for item in patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800999 tf, sf, size, _ = item
Doug Zongkere92f15a2011-08-26 13:46:40 -07001000 if tf.name == "system/build.prop":
1001 deferred_patch_list.append(item)
1002 continue
Michael Runge4038aa82013-12-13 18:06:28 -08001003 if (sf.name != tf.name):
1004 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1005 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Doug Zongker881dd402009-09-20 14:03:55 -07001006 so_far += tf.size
1007 script.SetProgress(so_far / total_patch_size)
1008
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001009 if not OPTIONS.two_step:
1010 if updating_boot:
1011 # Produce the boot image by applying a patch to the current
1012 # contents of the boot partition, and write it back to the
1013 # partition.
1014 script.Print("Patching boot image...")
1015 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1016 % (boot_type, boot_device,
1017 source_boot.size, source_boot.sha1,
1018 target_boot.size, target_boot.sha1),
1019 "-",
1020 target_boot.size, target_boot.sha1,
1021 source_boot.sha1, "patch/boot.img.p")
1022 so_far += target_boot.size
1023 script.SetProgress(so_far / total_patch_size)
1024 print "boot image changed; including."
1025 else:
1026 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001027
1028 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001029 # Recovery is generated as a patch using both the boot image
1030 # (which contains the same linux kernel as recovery) and the file
1031 # /system/etc/recovery-resource.dat (which contains all the images
1032 # used in the recovery UI) as sources. This lets us minimize the
1033 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001034 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001035 # For older builds where recovery-resource.dat is not present, we
1036 # use only the boot image as the source.
1037
Doug Zongkerc9253822014-02-04 12:17:58 -08001038 if not target_has_recovery_patch:
1039 def output_sink(fn, data):
1040 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
1041 Item.Get("system/" + fn, dir=False)
1042
1043 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1044 target_recovery, target_boot)
1045 script.DeleteFiles(["/system/recovery-from-boot.p",
1046 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001047 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001048 else:
1049 print "recovery image unchanged; skipping."
1050
Doug Zongker881dd402009-09-20 14:03:55 -07001051 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001052
Doug Zongker1807e702012-02-28 12:21:08 -08001053 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -07001054
1055 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001056 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -07001057 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -07001058 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001059
1060 # Note that this call will mess up the tree of Items, so make sure
1061 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -08001062 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -07001063 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1064
1065 # Delete all the symlinks in source that aren't in target. This
1066 # needs to happen before verbatim files are unpacked, in case a
1067 # symlink in the source is replaced by a real file in the target.
1068 to_delete = []
1069 for dest, link in source_symlinks:
1070 if link not in target_symlinks_d:
1071 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001072 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001073
1074 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001075 script.Print("Unpacking new files...")
1076 script.UnpackPackageDir("system", "/system")
1077
Doug Zongkerc9253822014-02-04 12:17:58 -08001078 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001079 script.Print("Unpacking new recovery...")
1080 script.UnpackPackageDir("recovery", "/system")
1081
Michael Runge4038aa82013-12-13 18:06:28 -08001082 if len(renames) > 0:
1083 script.Print("Renaming files...")
1084
1085 for src in renames:
1086 print "Renaming " + src + " to " + renames[src].name
1087 script.RenameFile(src, renames[src].name)
1088
Doug Zongker05d3dea2009-06-22 11:32:31 -07001089 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001090
1091 # Create all the symlinks that don't already exist, or point to
1092 # somewhere different than what we want. Delete each symlink before
1093 # creating it, since the 'symlink' command won't overwrite.
1094 to_create = []
1095 for dest, link in target_symlinks:
1096 if link in source_symlinks_d:
1097 if dest != source_symlinks_d[link]:
1098 to_create.append((dest, link))
1099 else:
1100 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001101 script.DeleteFiles([i[1] for i in to_create])
1102 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001103
1104 # Now that the symlinks are created, we can set all the
1105 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001106 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001107
Doug Zongker881dd402009-09-20 14:03:55 -07001108 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001109 device_specific.IncrementalOTA_InstallEnd()
1110
Doug Zongker1c390a22009-05-14 19:06:36 -07001111 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001112 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001113
Doug Zongkere92f15a2011-08-26 13:46:40 -07001114 # Patch the build.prop file last, so if something fails but the
1115 # device can still come up, it appears to be the old build and will
1116 # get set the OTA package again to retry.
1117 script.Print("Patching remaining system files...")
1118 for item in deferred_patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -08001119 tf, sf, size, _ = item
1120 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -07001121 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001122
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001123 if OPTIONS.two_step:
1124 script.AppendExtra("""
1125set_stage("%(bcb_dev)s", "");
1126endif;
1127endif;
1128""" % bcb_dev)
1129
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001130 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -07001131 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001132
1133
1134def main(argv):
1135
1136 def option_handler(o, a):
1137 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001138 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001139 elif o in ("-k", "--package_key"):
1140 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001141 elif o in ("-i", "--incremental_from"):
1142 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001143 elif o in ("-w", "--wipe_user_data"):
1144 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001145 elif o in ("-n", "--no_prereq"):
1146 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001147 elif o in ("-e", "--extra_script"):
1148 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001149 elif o in ("-a", "--aslr_mode"):
1150 if a in ("on", "On", "true", "True", "yes", "Yes"):
1151 OPTIONS.aslr_mode = True
1152 else:
1153 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -07001154 elif o in ("--worker_threads"):
1155 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001156 elif o in ("-2", "--two_step"):
1157 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001158 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001159 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001160 elif o == "--block":
1161 OPTIONS.block_based = True
Doug Zongkereef39442009-04-02 12:14:19 -07001162 else:
1163 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001164 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001165
1166 args = common.ParseOptions(argv, __doc__,
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001167 extra_opts="b:k:i:d:wne:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -07001168 extra_long_opts=["board_config=",
1169 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001170 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001171 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001172 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001173 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001174 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001175 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001176 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001177 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001178 "block",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001179 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001180 extra_option_handler=option_handler)
1181
1182 if len(args) != 2:
1183 common.Usage(__doc__)
1184 sys.exit(1)
1185
Doug Zongker1c390a22009-05-14 19:06:36 -07001186 if OPTIONS.extra_script is not None:
1187 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1188
Doug Zongkereef39442009-04-02 12:14:19 -07001189 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001190 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001191
Doug Zongkereef39442009-04-02 12:14:19 -07001192 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001193 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001194
1195 # If this image was originally labelled with SELinux contexts, make sure we
1196 # also apply the labels in our new image. During building, the "file_contexts"
1197 # is in the out/ directory tree, but for repacking from target-files.zip it's
1198 # in the root directory of the ramdisk.
1199 if "selinux_fc" in OPTIONS.info_dict:
1200 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1201 "file_contexts")
1202
Doug Zongker37974732010-09-16 17:44:38 -07001203 if OPTIONS.verbose:
1204 print "--- target info ---"
1205 common.DumpInfoDict(OPTIONS.info_dict)
1206
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001207 # If the caller explicitly specified the device-specific extensions
1208 # path via -s/--device_specific, use that. Otherwise, use
1209 # META/releasetools.py if it is present in the target target_files.
1210 # Otherwise, take the path of the file from 'tool_extensions' in the
1211 # info dict and look for that in the local filesystem, relative to
1212 # the current directory.
1213
Doug Zongker37974732010-09-16 17:44:38 -07001214 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001215 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1216 if os.path.exists(from_input):
1217 print "(using device-specific extensions from target_files)"
1218 OPTIONS.device_specific = from_input
1219 else:
1220 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1221
Doug Zongker37974732010-09-16 17:44:38 -07001222 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001223 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001224
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001225 if OPTIONS.no_signing:
1226 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1227 else:
1228 temp_zip_file = tempfile.NamedTemporaryFile()
1229 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1230 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001231
1232 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001233 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001234 if OPTIONS.package_key is None:
1235 OPTIONS.package_key = OPTIONS.info_dict.get(
1236 "default_system_dev_certificate",
1237 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001238 else:
1239 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001240 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001241 OPTIONS.target_info_dict = OPTIONS.info_dict
1242 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001243 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001244 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001245 "default_system_dev_certificate",
1246 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001247 if OPTIONS.verbose:
1248 print "--- source info ---"
1249 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001250 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001251
1252 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001253
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001254 if not OPTIONS.no_signing:
1255 SignOutput(temp_zip_file.name, args[1])
1256 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001257
1258 common.Cleanup()
1259
1260 print "done."
1261
1262
1263if __name__ == '__main__':
1264 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001265 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001266 main(sys.argv[1:])
1267 except common.ExternalError, e:
1268 print
1269 print " ERROR: %s" % (e,)
1270 print
1271 sys.exit(1)