blob: 79c5465267aee92eb6b79e95eef67bdf6ebd1c5f [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
27 -k (--package_key) <key>
28 Key to use to sign the package (default is
29 "build/target/product/security/testkey").
30
31 -i (--incremental_from) <file>
32 Generate an incremental OTA using the given target-files zip as
33 the starting build.
34
Doug Zongkerdbfaae52009-04-21 17:12:54 -070035 -w (--wipe_user_data)
36 Generate an OTA package that will wipe the user data partition
37 when installed.
38
Doug Zongker962069c2009-04-23 11:41:58 -070039 -n (--no_prereq)
40 Omit the timestamp prereq check normally included at the top of
41 the build scripts (used for developer OTA packages which
42 legitimately need to go back and forth).
43
Doug Zongker1c390a22009-05-14 19:06:36 -070044 -e (--extra_script) <file>
45 Insert the contents of file at the end of the update script.
46
Hristo Bojinovdafb0422010-08-26 14:35:16 -070047 -a (--aslr_mode) <on|off>
48 Specify whether to turn on ASLR for the package (on by default).
Doug Zongkereef39442009-04-02 12:14:19 -070049"""
50
51import sys
52
53if sys.hexversion < 0x02040000:
54 print >> sys.stderr, "Python 2.4 or newer is required."
55 sys.exit(1)
56
57import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070058import errno
Doug Zongkereef39442009-04-02 12:14:19 -070059import os
60import re
Doug Zongkereef39442009-04-02 12:14:19 -070061import subprocess
62import tempfile
63import time
64import zipfile
65
66import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070067import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070068
69OPTIONS = common.OPTIONS
70OPTIONS.package_key = "build/target/product/security/testkey"
71OPTIONS.incremental_source = None
72OPTIONS.require_verbatim = set()
73OPTIONS.prohibit_verbatim = set(("system/build.prop",))
74OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070075OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070076OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070077OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070078OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070079OPTIONS.worker_threads = 3
Doug Zongkereef39442009-04-02 12:14:19 -070080
81def MostPopularKey(d, default):
82 """Given a dict, return the key corresponding to the largest
83 value. Returns 'default' if the dict is empty."""
84 x = [(v, k) for (k, v) in d.iteritems()]
85 if not x: return default
86 x.sort()
87 return x[-1][1]
88
89
90def IsSymlink(info):
91 """Return true if the zipfile.ZipInfo object passed in represents a
92 symlink."""
93 return (info.external_attr >> 16) == 0120777
94
Hristo Bojinov96be7202010-08-02 10:26:17 -070095def IsRegular(info):
96 """Return true if the zipfile.ZipInfo object passed in represents a
97 symlink."""
98 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -070099
Doug Zongkereef39442009-04-02 12:14:19 -0700100class Item:
101 """Items represent the metadata (user, group, mode) of files and
102 directories in the system image."""
103 ITEMS = {}
104 def __init__(self, name, dir=False):
105 self.name = name
106 self.uid = None
107 self.gid = None
108 self.mode = None
109 self.dir = dir
110
111 if name:
112 self.parent = Item.Get(os.path.dirname(name), dir=True)
113 self.parent.children.append(self)
114 else:
115 self.parent = None
116 if dir:
117 self.children = []
118
119 def Dump(self, indent=0):
120 if self.uid is not None:
121 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
122 else:
123 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
124 if self.dir:
125 print "%s%s" % (" "*indent, self.descendants)
126 print "%s%s" % (" "*indent, self.best_subtree)
127 for i in self.children:
128 i.Dump(indent=indent+1)
129
130 @classmethod
131 def Get(cls, name, dir=False):
132 if name not in cls.ITEMS:
133 cls.ITEMS[name] = Item(name, dir=dir)
134 return cls.ITEMS[name]
135
136 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700137 def GetMetadata(cls, input_zip):
138
139 try:
140 # See if the target_files contains a record of what the uid,
141 # gid, and mode is supposed to be.
142 output = input_zip.read("META/filesystem_config.txt")
143 except KeyError:
144 # Run the external 'fs_config' program to determine the desired
145 # uid, gid, and mode for every Item object. Note this uses the
146 # one in the client now, which might not be the same as the one
147 # used when this target_files was built.
148 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
149 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
150 suffix = { False: "", True: "/" }
151 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
152 for i in cls.ITEMS.itervalues() if i.name])
Doug Zongker3475d362010-03-17 16:39:30 -0700153 output, error = p.communicate(input)
Doug Zongker283e2a12010-03-15 17:52:32 -0700154 assert not error
Doug Zongkereef39442009-04-02 12:14:19 -0700155
156 for line in output.split("\n"):
157 if not line: continue
158 name, uid, gid, mode = line.split()
Doug Zongker283e2a12010-03-15 17:52:32 -0700159 i = cls.ITEMS.get(name, None)
160 if i is not None:
161 i.uid = int(uid)
162 i.gid = int(gid)
163 i.mode = int(mode, 8)
164 if i.dir:
165 i.children.sort(key=lambda i: i.name)
166
167 # set metadata for the files generated by this script.
168 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
169 if i: i.uid, i.gid, i.mode = 0, 0, 0644
170 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
171 if i: i.uid, i.gid, i.mode = 0, 0, 0544
Doug Zongkereef39442009-04-02 12:14:19 -0700172
173 def CountChildMetadata(self):
174 """Count up the (uid, gid, mode) tuples for all children and
175 determine the best strategy for using set_perm_recursive and
176 set_perm to correctly chown/chmod all the files to their desired
177 values. Recursively calls itself for all descendants.
178
179 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
180 all descendants of this node. (dmode or fmode may be None.) Also
181 sets the best_subtree of each directory Item to the (uid, gid,
182 dmode, fmode) tuple that will match the most descendants of that
183 Item.
184 """
185
186 assert self.dir
187 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
188 for i in self.children:
189 if i.dir:
190 for k, v in i.CountChildMetadata().iteritems():
191 d[k] = d.get(k, 0) + v
192 else:
193 k = (i.uid, i.gid, None, i.mode)
194 d[k] = d.get(k, 0) + 1
195
196 # Find the (uid, gid, dmode, fmode) tuple that matches the most
197 # descendants.
198
199 # First, find the (uid, gid) pair that matches the most
200 # descendants.
201 ug = {}
202 for (uid, gid, _, _), count in d.iteritems():
203 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
204 ug = MostPopularKey(ug, (0, 0))
205
206 # Now find the dmode and fmode that match the most descendants
207 # with that (uid, gid), and choose those.
208 best_dmode = (0, 0755)
209 best_fmode = (0, 0644)
210 for k, count in d.iteritems():
211 if k[:2] != ug: continue
212 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
213 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
214 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
215
216 return d
217
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700218 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700219 """Append set_perm/set_perm_recursive commands to 'script' to
220 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700221 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700222
223 self.CountChildMetadata()
224
225 def recurse(item, current):
226 # current is the (uid, gid, dmode, fmode) tuple that the current
227 # item (and all its children) have already been set to. We only
228 # need to issue set_perm/set_perm_recursive commands if we're
229 # supposed to be something different.
230 if item.dir:
231 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700232 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700233 current = item.best_subtree
234
235 if item.uid != current[0] or item.gid != current[1] or \
236 item.mode != current[2]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700237 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700238
239 for i in item.children:
240 recurse(i, current)
241 else:
242 if item.uid != current[0] or item.gid != current[1] or \
243 item.mode != current[3]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700244 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700245
246 recurse(self, (-1, -1, -1, -1))
247
248
249def CopySystemFiles(input_zip, output_zip=None,
250 substitute=None):
251 """Copies files underneath system/ in the input zip to the output
252 zip. Populates the Item class with their metadata, and returns a
Hristo Bojinov96be7202010-08-02 10:26:17 -0700253 list of symlinks as well as a list of files that will be retouched.
254 output_zip may be None, in which case the copy is skipped (but the
255 other side effects still happen). substitute is an optional dict
256 of {output filename: contents} to be output instead of certain input
257 files.
Doug Zongkereef39442009-04-02 12:14:19 -0700258 """
259
260 symlinks = []
Hristo Bojinov96be7202010-08-02 10:26:17 -0700261 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700262
263 for info in input_zip.infolist():
264 if info.filename.startswith("SYSTEM/"):
265 basefilename = info.filename[7:]
266 if IsSymlink(info):
267 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700268 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700269 else:
270 info2 = copy.copy(info)
271 fn = info2.filename = "system/" + basefilename
272 if substitute and fn in substitute and substitute[fn] is None:
273 continue
274 if output_zip is not None:
275 if substitute and fn in substitute:
276 data = substitute[fn]
277 else:
278 data = input_zip.read(info.filename)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700279 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
280 retouch_files.append(("/system/" + basefilename,
Doug Zongker55d93282011-01-25 17:03:34 -0800281 common.sha1(data).hexdigest()))
Doug Zongkereef39442009-04-02 12:14:19 -0700282 output_zip.writestr(info2, data)
283 if fn.endswith("/"):
284 Item.Get(fn[:-1], dir=True)
285 else:
286 Item.Get(fn, dir=False)
287
288 symlinks.sort()
Hristo Bojinov96be7202010-08-02 10:26:17 -0700289 return (symlinks, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700290
291
Doug Zongkereef39442009-04-02 12:14:19 -0700292def SignOutput(temp_zip_name, output_zip_name):
293 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
294 pw = key_passwords[OPTIONS.package_key]
295
Doug Zongker951495f2009-08-14 12:44:19 -0700296 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
297 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700298
299
Doug Zongkereef39442009-04-02 12:14:19 -0700300def AppendAssertions(script, input_zip):
Doug Zongkereef39442009-04-02 12:14:19 -0700301 device = GetBuildProp("ro.product.device", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700302 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700303
Doug Zongkereef39442009-04-02 12:14:19 -0700304
Doug Zongker486de122010-09-16 14:01:56 -0700305def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700306 """Generate a binary patch that creates the recovery image starting
307 with the boot image. (Most of the space in these images is just the
308 kernel, which is identical for the two, so the resulting patch
309 should be efficient.) Add it to the output zip, along with a shell
310 script that is run from init.rc on first boot to actually do the
311 patching and install the new recovery image.
312
313 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700314 corresponding images. info should be the dictionary returned by
315 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700316
317 Returns an Item for the shell script, which must be made
318 executable.
319 """
320
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700321 d = common.Difference(recovery_img, boot_img)
Doug Zongker761e6422009-09-25 10:45:39 -0700322 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700323 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700324 Item.Get("system/recovery-from-boot.p", dir=False)
325
Doug Zongker96a57e72010-09-26 14:57:41 -0700326 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
327 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700328
Doug Zongker73ef8252009-07-23 15:12:53 -0700329 # Images with different content will have a different first page, so
330 # we check to see if this recovery has already been installed by
331 # testing just the first 2k.
332 HEADER_SIZE = 2048
Doug Zongker55d93282011-01-25 17:03:34 -0800333 header_sha1 = common.sha1(recovery_img.data[:HEADER_SIZE]).hexdigest()
Doug Zongker73ef8252009-07-23 15:12:53 -0700334 sh = """#!/system/bin/sh
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700335if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(header_size)d:%(header_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700336 log -t recovery "Installing new recovery image"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700337 applypatch %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
Doug Zongker73ef8252009-07-23 15:12:53 -0700338else
339 log -t recovery "Recovery image already installed"
340fi
341""" % { 'boot_size': boot_img.size,
342 'boot_sha1': boot_img.sha1,
343 'header_size': HEADER_SIZE,
344 'header_sha1': header_sha1,
345 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700346 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700347 'boot_type': boot_type,
348 'boot_device': boot_device,
349 'recovery_type': recovery_type,
350 'recovery_device': recovery_device,
Doug Zongker67369982010-07-07 13:53:32 -0700351 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700352 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700353 return Item.Get("system/etc/install-recovery.sh", dir=False)
354
355
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700356def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700357 # TODO: how to determine this? We don't know what version it will
358 # be installed on top of. For now, we expect the API just won't
359 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700360 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700361
Doug Zongker2ea21062010-04-28 16:05:21 -0700362 metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
363 "pre-device": GetBuildProp("ro.product.device", input_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700364 "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700365 }
366
Doug Zongker05d3dea2009-06-22 11:32:31 -0700367 device_specific = common.DeviceSpecificParams(
368 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700369 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700370 output_zip=output_zip,
371 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700372 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700373 metadata=metadata,
374 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700375
Doug Zongker962069c2009-04-23 11:41:58 -0700376 if not OPTIONS.omit_prereq:
377 ts = GetBuildProp("ro.build.date.utc", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700378 script.AssertOlderBuild(ts)
Doug Zongkereef39442009-04-02 12:14:19 -0700379
380 AppendAssertions(script, input_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700381 device_specific.FullOTA_Assertions()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700382
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700383 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700384
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700385 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700386 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700387
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700388 script.FormatPartition("/system")
389 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700390 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700391 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700392
Hristo Bojinov96be7202010-08-02 10:26:17 -0700393 (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700394 script.MakeSymlinks(symlinks)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700395 if OPTIONS.aslr_mode:
396 script.RetouchBinaries(retouch_files)
397 else:
398 script.UndoRetouchBinaries(retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700399
Doug Zongker55d93282011-01-25 17:03:34 -0800400 boot_img = common.GetBootableImage("boot.img", "boot.img",
401 OPTIONS.input_tmp, "BOOT")
402 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
403 OPTIONS.input_tmp, "RECOVERY")
Doug Zongker486de122010-09-16 14:01:56 -0700404 MakeRecoveryPatch(output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700405
Doug Zongker283e2a12010-03-15 17:52:32 -0700406 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700407 Item.Get("system").SetPermissions(script)
408
Doug Zongker37974732010-09-16 17:44:38 -0700409 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700410 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700411 script.ShowProgress(0.2, 0)
412
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700413 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700414 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700415
416 script.ShowProgress(0.1, 0)
417 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700418
Doug Zongker1c390a22009-05-14 19:06:36 -0700419 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700420 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700421
Doug Zongker14833602010-02-02 13:12:04 -0800422 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700423 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700424 WriteMetadata(metadata, output_zip)
425
426
427def WriteMetadata(metadata, output_zip):
428 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
429 "".join(["%s=%s\n" % kv
430 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700431
432
Doug Zongkereef39442009-04-02 12:14:19 -0700433
434
435def LoadSystemFiles(z):
436 """Load all the files from SYSTEM/... in a given target-files
437 ZipFile, and return a dict of {filename: File object}."""
438 out = {}
Hristo Bojinov96be7202010-08-02 10:26:17 -0700439 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700440 for info in z.infolist():
441 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700442 basefilename = info.filename[7:]
443 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700444 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700445 out[fn] = common.File(fn, data)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700446 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
447 retouch_files.append(("/system/" + basefilename,
448 out[fn].sha1))
449 return (out, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700450
451
Doug Zongkereef39442009-04-02 12:14:19 -0700452def GetBuildProp(property, z):
453 """Return the fingerprint of the build of a given target-files
454 ZipFile object."""
455 bp = z.read("SYSTEM/build.prop")
456 if not property:
457 return bp
458 m = re.search(re.escape(property) + r"=(.*)\n", bp)
459 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700460 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700461 return m.group(1).strip()
462
463
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700464def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700465 source_version = OPTIONS.source_info_dict["recovery_api_version"]
466 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700467
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700468 if source_version == 0:
469 print ("WARNING: generating edify script for a source that "
470 "can't install it.")
Doug Zongkerb984ae52010-09-16 23:13:11 -0700471 script = edify_generator.EdifyGenerator(source_version, OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700472
Doug Zongker2ea21062010-04-28 16:05:21 -0700473 metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700474 "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700475 }
476
Doug Zongker05d3dea2009-06-22 11:32:31 -0700477 device_specific = common.DeviceSpecificParams(
478 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800479 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700480 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800481 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700482 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700483 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700484 metadata=metadata,
485 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700486
Doug Zongkereef39442009-04-02 12:14:19 -0700487 print "Loading target..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700488 (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700489 print "Loading source..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700490 (source_data, source_retouch_files) = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700491
492 verbatim_targets = []
493 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700494 diffs = []
Doug Zongkereef39442009-04-02 12:14:19 -0700495 largest_source_size = 0
496 for fn in sorted(target_data.keys()):
497 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700498 assert fn == tf.name
Doug Zongkereef39442009-04-02 12:14:19 -0700499 sf = source_data.get(fn, None)
500
501 if sf is None or fn in OPTIONS.require_verbatim:
502 # This file should be included verbatim
503 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700504 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700505 print "send", fn, "verbatim"
506 tf.AddToZip(output_zip)
507 verbatim_targets.append((fn, tf.size))
508 elif tf.sha1 != sf.sha1:
509 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700510 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700511 else:
512 # Target file identical to source.
513 pass
514
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700515 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700516
517 for diff in diffs:
518 tf, sf, d = diff.GetPatch()
519 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
520 # patch is almost as big as the file; don't bother patching
521 tf.AddToZip(output_zip)
522 verbatim_targets.append((tf.name, tf.size))
523 else:
524 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
Doug Zongker55d93282011-01-25 17:03:34 -0800525 patch_list.append((tf.name, tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700526 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700527
528 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
529 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700530 metadata["pre-build"] = source_fp
531 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700532
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700533 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700534 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700535
Doug Zongker55d93282011-01-25 17:03:34 -0800536 source_boot = common.GetBootableImage(
537 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT")
538 target_boot = common.GetBootableImage(
539 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker5da317e2009-06-02 13:38:17 -0700540 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700541
Doug Zongker55d93282011-01-25 17:03:34 -0800542 source_recovery = common.GetBootableImage(
543 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY")
544 target_recovery = common.GetBootableImage(
545 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700546 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700547
Doug Zongker881dd402009-09-20 14:03:55 -0700548 # Here's how we divide up the progress bar:
549 # 0.1 for verifying the start state (PatchCheck calls)
550 # 0.8 for applying patches (ApplyPatch calls)
551 # 0.1 for unpacking verbatim files, symlinking, and doing the
552 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700553
554 AppendAssertions(script, target_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700555 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700556
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700557 script.Print("Verifying current system...")
558
Doug Zongker881dd402009-09-20 14:03:55 -0700559 script.ShowProgress(0.1, 0)
560 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
561 if updating_boot:
562 total_verify_size += source_boot.size
563 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700564
Doug Zongker5a482092010-02-17 16:09:18 -0800565 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700566 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700567 so_far += sf.size
568 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700569
Doug Zongker5da317e2009-06-02 13:38:17 -0700570 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700571 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700572 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700573 print "boot target: %d source: %d diff: %d" % (
574 target_boot.size, source_boot.size, len(d))
575
Doug Zongker048e7ca2009-06-15 14:31:53 -0700576 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700577
Doug Zongker96a57e72010-09-26 14:57:41 -0700578 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700579
580 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
581 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700582 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700583 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700584 so_far += source_boot.size
585 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700586
587 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700588 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800589
Doug Zongker05d3dea2009-06-22 11:32:31 -0700590 device_specific.IncrementalOTA_VerifyEnd()
591
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700592 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700593
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700594 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700595 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700596 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700597
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700598 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700599 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
600 ["/"+i for i in sorted(source_data)
Doug Zongker3b949f02009-08-24 10:24:32 -0700601 if i not in target_data] +
602 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700603
Doug Zongker881dd402009-09-20 14:03:55 -0700604 script.ShowProgress(0.8, 0)
605 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
606 if updating_boot:
607 total_patch_size += target_boot.size
608 so_far = 0
609
610 script.Print("Patching system files...")
Doug Zongker5a482092010-02-17 16:09:18 -0800611 for fn, tf, sf, size, _ in patch_list:
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800612 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700613 so_far += tf.size
614 script.SetProgress(so_far / total_patch_size)
615
Doug Zongkereef39442009-04-02 12:14:19 -0700616 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700617 # Produce the boot image by applying a patch to the current
618 # contents of the boot partition, and write it back to the
619 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700620 script.Print("Patching boot image...")
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700621 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
622 % (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700623 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700624 target_boot.size, target_boot.sha1),
625 "-",
626 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800627 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700628 so_far += target_boot.size
629 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700630 print "boot image changed; including."
631 else:
632 print "boot image unchanged; skipping."
633
634 if updating_recovery:
Doug Zongker73ef8252009-07-23 15:12:53 -0700635 # Is it better to generate recovery as a patch from the current
636 # boot image, or from the previous recovery image? For large
637 # updates with significant kernel changes, probably the former.
638 # For small updates where the kernel hasn't changed, almost
639 # certainly the latter. We pick the first option. Future
640 # complicated schemes may let us effectively use both.
641 #
642 # A wacky possibility: as long as there is room in the boot
643 # partition, include the binaries and image files from recovery in
644 # the boot image (though not in the ramdisk) so they can be used
645 # as fodder for constructing the recovery image.
Doug Zongker486de122010-09-16 14:01:56 -0700646 MakeRecoveryPatch(output_zip, target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800647 script.DeleteFiles(["/system/recovery-from-boot.p",
648 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700649 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700650 else:
651 print "recovery image unchanged; skipping."
652
Doug Zongker881dd402009-09-20 14:03:55 -0700653 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700654
Hristo Bojinov96be7202010-08-02 10:26:17 -0700655 (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700656
657 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700658 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700659 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700660 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700661
662 # Note that this call will mess up the tree of Items, so make sure
663 # we're done with it.
Hristo Bojinov96be7202010-08-02 10:26:17 -0700664 (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700665 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
666
667 # Delete all the symlinks in source that aren't in target. This
668 # needs to happen before verbatim files are unpacked, in case a
669 # symlink in the source is replaced by a real file in the target.
670 to_delete = []
671 for dest, link in source_symlinks:
672 if link not in target_symlinks_d:
673 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700674 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700675
676 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700677 script.Print("Unpacking new files...")
678 script.UnpackPackageDir("system", "/system")
679
Doug Zongker42265392010-02-12 10:21:00 -0800680 if updating_recovery:
681 script.Print("Unpacking new recovery...")
682 script.UnpackPackageDir("recovery", "/system")
683
Doug Zongker05d3dea2009-06-22 11:32:31 -0700684 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700685
686 # Create all the symlinks that don't already exist, or point to
687 # somewhere different than what we want. Delete each symlink before
688 # creating it, since the 'symlink' command won't overwrite.
689 to_create = []
690 for dest, link in target_symlinks:
691 if link in source_symlinks_d:
692 if dest != source_symlinks_d[link]:
693 to_create.append((dest, link))
694 else:
695 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700696 script.DeleteFiles([i[1] for i in to_create])
697 script.MakeSymlinks(to_create)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700698 if OPTIONS.aslr_mode:
699 script.RetouchBinaries(target_retouch_files)
700 else:
701 script.UndoRetouchBinaries(target_retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700702
703 # Now that the symlinks are created, we can set all the
704 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700705 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700706
Doug Zongker881dd402009-09-20 14:03:55 -0700707 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700708 device_specific.IncrementalOTA_InstallEnd()
709
Doug Zongker1c390a22009-05-14 19:06:36 -0700710 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700711 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700712
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700713 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700714 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700715
716
717def main(argv):
718
719 def option_handler(o, a):
720 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700721 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700722 elif o in ("-k", "--package_key"):
723 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700724 elif o in ("-i", "--incremental_from"):
725 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700726 elif o in ("-w", "--wipe_user_data"):
727 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700728 elif o in ("-n", "--no_prereq"):
729 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700730 elif o in ("-e", "--extra_script"):
731 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700732 elif o in ("-a", "--aslr_mode"):
733 if a in ("on", "On", "true", "True", "yes", "Yes"):
734 OPTIONS.aslr_mode = True
735 else:
736 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700737 elif o in ("--worker_threads"):
738 OPTIONS.worker_threads = int(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700739 else:
740 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700741 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700742
743 args = common.ParseOptions(argv, __doc__,
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700744 extra_opts="b:k:i:d:wne:a:",
Doug Zongkereef39442009-04-02 12:14:19 -0700745 extra_long_opts=["board_config=",
746 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700747 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700748 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700749 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700750 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700751 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700752 "aslr_mode=",
753 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700754 extra_option_handler=option_handler)
755
756 if len(args) != 2:
757 common.Usage(__doc__)
758 sys.exit(1)
759
Doug Zongker1c390a22009-05-14 19:06:36 -0700760 if OPTIONS.extra_script is not None:
761 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
762
Doug Zongkereef39442009-04-02 12:14:19 -0700763 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800764 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700765
Doug Zongkereef39442009-04-02 12:14:19 -0700766 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -0700767 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
768 if OPTIONS.verbose:
769 print "--- target info ---"
770 common.DumpInfoDict(OPTIONS.info_dict)
771
772 if OPTIONS.device_specific is None:
773 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
774 if OPTIONS.device_specific is not None:
775 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
776 print "using device-specific extensions in", OPTIONS.device_specific
777
Doug Zongkereef39442009-04-02 12:14:19 -0700778 if OPTIONS.package_key:
779 temp_zip_file = tempfile.NamedTemporaryFile()
780 output_zip = zipfile.ZipFile(temp_zip_file, "w",
781 compression=zipfile.ZIP_DEFLATED)
782 else:
783 output_zip = zipfile.ZipFile(args[1], "w",
784 compression=zipfile.ZIP_DEFLATED)
785
786 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700787 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700788 else:
789 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800790 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -0700791 OPTIONS.target_info_dict = OPTIONS.info_dict
792 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
793 if OPTIONS.verbose:
794 print "--- source info ---"
795 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700796 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700797
798 output_zip.close()
799 if OPTIONS.package_key:
800 SignOutput(temp_zip_file.name, args[1])
801 temp_zip_file.close()
802
803 common.Cleanup()
804
805 print "done."
806
807
808if __name__ == '__main__':
809 try:
810 main(sys.argv[1:])
811 except common.ExternalError, e:
812 print
813 print " ERROR: %s" % (e,)
814 print
815 sys.exit(1)