blob: 9aeee2759cefec9b67985037eac492e26f109a67 [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
61import sha
62import subprocess
63import tempfile
64import time
65import zipfile
66
67import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070068import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070069
70OPTIONS = common.OPTIONS
71OPTIONS.package_key = "build/target/product/security/testkey"
72OPTIONS.incremental_source = None
73OPTIONS.require_verbatim = set()
74OPTIONS.prohibit_verbatim = set(("system/build.prop",))
75OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070076OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070077OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070078OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070079OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070080OPTIONS.worker_threads = 3
Doug Zongkereef39442009-04-02 12:14:19 -070081
82def MostPopularKey(d, default):
83 """Given a dict, return the key corresponding to the largest
84 value. Returns 'default' if the dict is empty."""
85 x = [(v, k) for (k, v) in d.iteritems()]
86 if not x: return default
87 x.sort()
88 return x[-1][1]
89
90
91def IsSymlink(info):
92 """Return true if the zipfile.ZipInfo object passed in represents a
93 symlink."""
94 return (info.external_attr >> 16) == 0120777
95
Hristo Bojinov96be7202010-08-02 10:26:17 -070096def IsRegular(info):
97 """Return true if the zipfile.ZipInfo object passed in represents a
98 symlink."""
99 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700100
Doug Zongkereef39442009-04-02 12:14:19 -0700101class Item:
102 """Items represent the metadata (user, group, mode) of files and
103 directories in the system image."""
104 ITEMS = {}
105 def __init__(self, name, dir=False):
106 self.name = name
107 self.uid = None
108 self.gid = None
109 self.mode = None
110 self.dir = dir
111
112 if name:
113 self.parent = Item.Get(os.path.dirname(name), dir=True)
114 self.parent.children.append(self)
115 else:
116 self.parent = None
117 if dir:
118 self.children = []
119
120 def Dump(self, indent=0):
121 if self.uid is not None:
122 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
123 else:
124 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
125 if self.dir:
126 print "%s%s" % (" "*indent, self.descendants)
127 print "%s%s" % (" "*indent, self.best_subtree)
128 for i in self.children:
129 i.Dump(indent=indent+1)
130
131 @classmethod
132 def Get(cls, name, dir=False):
133 if name not in cls.ITEMS:
134 cls.ITEMS[name] = Item(name, dir=dir)
135 return cls.ITEMS[name]
136
137 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700138 def GetMetadata(cls, input_zip):
139
140 try:
141 # See if the target_files contains a record of what the uid,
142 # gid, and mode is supposed to be.
143 output = input_zip.read("META/filesystem_config.txt")
144 except KeyError:
145 # Run the external 'fs_config' program to determine the desired
146 # uid, gid, and mode for every Item object. Note this uses the
147 # one in the client now, which might not be the same as the one
148 # used when this target_files was built.
149 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
150 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
151 suffix = { False: "", True: "/" }
152 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
153 for i in cls.ITEMS.itervalues() if i.name])
Doug Zongker3475d362010-03-17 16:39:30 -0700154 output, error = p.communicate(input)
Doug Zongker283e2a12010-03-15 17:52:32 -0700155 assert not error
Doug Zongkereef39442009-04-02 12:14:19 -0700156
157 for line in output.split("\n"):
158 if not line: continue
159 name, uid, gid, mode = line.split()
Doug Zongker283e2a12010-03-15 17:52:32 -0700160 i = cls.ITEMS.get(name, None)
161 if i is not None:
162 i.uid = int(uid)
163 i.gid = int(gid)
164 i.mode = int(mode, 8)
165 if i.dir:
166 i.children.sort(key=lambda i: i.name)
167
168 # set metadata for the files generated by this script.
169 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
170 if i: i.uid, i.gid, i.mode = 0, 0, 0644
171 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
172 if i: i.uid, i.gid, i.mode = 0, 0, 0544
Doug Zongkereef39442009-04-02 12:14:19 -0700173
174 def CountChildMetadata(self):
175 """Count up the (uid, gid, mode) tuples for all children and
176 determine the best strategy for using set_perm_recursive and
177 set_perm to correctly chown/chmod all the files to their desired
178 values. Recursively calls itself for all descendants.
179
180 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
181 all descendants of this node. (dmode or fmode may be None.) Also
182 sets the best_subtree of each directory Item to the (uid, gid,
183 dmode, fmode) tuple that will match the most descendants of that
184 Item.
185 """
186
187 assert self.dir
188 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
189 for i in self.children:
190 if i.dir:
191 for k, v in i.CountChildMetadata().iteritems():
192 d[k] = d.get(k, 0) + v
193 else:
194 k = (i.uid, i.gid, None, i.mode)
195 d[k] = d.get(k, 0) + 1
196
197 # Find the (uid, gid, dmode, fmode) tuple that matches the most
198 # descendants.
199
200 # First, find the (uid, gid) pair that matches the most
201 # descendants.
202 ug = {}
203 for (uid, gid, _, _), count in d.iteritems():
204 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
205 ug = MostPopularKey(ug, (0, 0))
206
207 # Now find the dmode and fmode that match the most descendants
208 # with that (uid, gid), and choose those.
209 best_dmode = (0, 0755)
210 best_fmode = (0, 0644)
211 for k, count in d.iteritems():
212 if k[:2] != ug: continue
213 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
214 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
215 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
216
217 return d
218
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700219 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700220 """Append set_perm/set_perm_recursive commands to 'script' to
221 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700222 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700223
224 self.CountChildMetadata()
225
226 def recurse(item, current):
227 # current is the (uid, gid, dmode, fmode) tuple that the current
228 # item (and all its children) have already been set to. We only
229 # need to issue set_perm/set_perm_recursive commands if we're
230 # supposed to be something different.
231 if item.dir:
232 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700233 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700234 current = item.best_subtree
235
236 if item.uid != current[0] or item.gid != current[1] or \
237 item.mode != current[2]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700238 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700239
240 for i in item.children:
241 recurse(i, current)
242 else:
243 if item.uid != current[0] or item.gid != current[1] or \
244 item.mode != current[3]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700245 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700246
247 recurse(self, (-1, -1, -1, -1))
248
249
250def CopySystemFiles(input_zip, output_zip=None,
251 substitute=None):
252 """Copies files underneath system/ in the input zip to the output
253 zip. Populates the Item class with their metadata, and returns a
Hristo Bojinov96be7202010-08-02 10:26:17 -0700254 list of symlinks as well as a list of files that will be retouched.
255 output_zip may be None, in which case the copy is skipped (but the
256 other side effects still happen). substitute is an optional dict
257 of {output filename: contents} to be output instead of certain input
258 files.
Doug Zongkereef39442009-04-02 12:14:19 -0700259 """
260
261 symlinks = []
Hristo Bojinov96be7202010-08-02 10:26:17 -0700262 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700263
264 for info in input_zip.infolist():
265 if info.filename.startswith("SYSTEM/"):
266 basefilename = info.filename[7:]
267 if IsSymlink(info):
268 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700269 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700270 else:
271 info2 = copy.copy(info)
272 fn = info2.filename = "system/" + basefilename
273 if substitute and fn in substitute and substitute[fn] is None:
274 continue
275 if output_zip is not None:
276 if substitute and fn in substitute:
277 data = substitute[fn]
278 else:
279 data = input_zip.read(info.filename)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700280 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
281 retouch_files.append(("/system/" + basefilename,
282 sha.sha(data).hexdigest()))
Doug Zongkereef39442009-04-02 12:14:19 -0700283 output_zip.writestr(info2, data)
284 if fn.endswith("/"):
285 Item.Get(fn[:-1], dir=True)
286 else:
287 Item.Get(fn, dir=False)
288
289 symlinks.sort()
Hristo Bojinov96be7202010-08-02 10:26:17 -0700290 return (symlinks, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700291
292
Doug Zongkereef39442009-04-02 12:14:19 -0700293def SignOutput(temp_zip_name, output_zip_name):
294 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
295 pw = key_passwords[OPTIONS.package_key]
296
Doug Zongker951495f2009-08-14 12:44:19 -0700297 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
298 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700299
300
Doug Zongkereef39442009-04-02 12:14:19 -0700301def AppendAssertions(script, input_zip):
Doug Zongkereef39442009-04-02 12:14:19 -0700302 device = GetBuildProp("ro.product.device", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700303 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700304
Doug Zongkereef39442009-04-02 12:14:19 -0700305
Doug Zongker486de122010-09-16 14:01:56 -0700306def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700307 """Generate a binary patch that creates the recovery image starting
308 with the boot image. (Most of the space in these images is just the
309 kernel, which is identical for the two, so the resulting patch
310 should be efficient.) Add it to the output zip, along with a shell
311 script that is run from init.rc on first boot to actually do the
312 patching and install the new recovery image.
313
314 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700315 corresponding images. info should be the dictionary returned by
316 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700317
318 Returns an Item for the shell script, which must be made
319 executable.
320 """
321
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700322 d = common.Difference(recovery_img, boot_img)
Doug Zongker761e6422009-09-25 10:45:39 -0700323 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700324 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700325 Item.Get("system/recovery-from-boot.p", dir=False)
326
Doug Zongker96a57e72010-09-26 14:57:41 -0700327 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
328 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700329
Doug Zongker73ef8252009-07-23 15:12:53 -0700330 # Images with different content will have a different first page, so
331 # we check to see if this recovery has already been installed by
332 # testing just the first 2k.
333 HEADER_SIZE = 2048
334 header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
335 sh = """#!/system/bin/sh
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700336if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(header_size)d:%(header_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700337 log -t recovery "Installing new recovery image"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700338 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 -0700339else
340 log -t recovery "Recovery image already installed"
341fi
342""" % { 'boot_size': boot_img.size,
343 'boot_sha1': boot_img.sha1,
344 'header_size': HEADER_SIZE,
345 'header_sha1': header_sha1,
346 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700347 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700348 'boot_type': boot_type,
349 'boot_device': boot_device,
350 'recovery_type': recovery_type,
351 'recovery_device': recovery_device,
Doug Zongker67369982010-07-07 13:53:32 -0700352 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700353 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700354 return Item.Get("system/etc/install-recovery.sh", dir=False)
355
356
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700357def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700358 # TODO: how to determine this? We don't know what version it will
359 # be installed on top of. For now, we expect the API just won't
360 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700361 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700362
Doug Zongker2ea21062010-04-28 16:05:21 -0700363 metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
364 "pre-device": GetBuildProp("ro.product.device", input_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700365 "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700366 }
367
Doug Zongker05d3dea2009-06-22 11:32:31 -0700368 device_specific = common.DeviceSpecificParams(
369 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700370 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700371 output_zip=output_zip,
372 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700373 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700374 metadata=metadata,
375 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700376
Doug Zongker962069c2009-04-23 11:41:58 -0700377 if not OPTIONS.omit_prereq:
378 ts = GetBuildProp("ro.build.date.utc", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700379 script.AssertOlderBuild(ts)
Doug Zongkereef39442009-04-02 12:14:19 -0700380
381 AppendAssertions(script, input_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700382 device_specific.FullOTA_Assertions()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700383
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700384 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700385
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700386 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700387 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700388
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700389 script.FormatPartition("/system")
390 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700391 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700392 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700393
Hristo Bojinov96be7202010-08-02 10:26:17 -0700394 (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700395 script.MakeSymlinks(symlinks)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700396 if OPTIONS.aslr_mode:
397 script.RetouchBinaries(retouch_files)
398 else:
399 script.UndoRetouchBinaries(retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700400
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700401 boot_img = common.File("boot.img", common.BuildBootableImage(
Doug Zongker73ef8252009-07-23 15:12:53 -0700402 os.path.join(OPTIONS.input_tmp, "BOOT")))
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700403 recovery_img = common.File("recovery.img", common.BuildBootableImage(
Doug Zongker73ef8252009-07-23 15:12:53 -0700404 os.path.join(OPTIONS.input_tmp, "RECOVERY")))
Doug Zongker486de122010-09-16 14:01:56 -0700405 MakeRecoveryPatch(output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700406
Doug Zongker283e2a12010-03-15 17:52:32 -0700407 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700408 Item.Get("system").SetPermissions(script)
409
Doug Zongker37974732010-09-16 17:44:38 -0700410 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700411 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700412 script.ShowProgress(0.2, 0)
413
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700414 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700415 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700416
417 script.ShowProgress(0.1, 0)
418 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700419
Doug Zongker1c390a22009-05-14 19:06:36 -0700420 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700421 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700422
Doug Zongker14833602010-02-02 13:12:04 -0800423 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700424 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700425 WriteMetadata(metadata, output_zip)
426
427
428def WriteMetadata(metadata, output_zip):
429 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
430 "".join(["%s=%s\n" % kv
431 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700432
433
Doug Zongkereef39442009-04-02 12:14:19 -0700434
435
436def LoadSystemFiles(z):
437 """Load all the files from SYSTEM/... in a given target-files
438 ZipFile, and return a dict of {filename: File object}."""
439 out = {}
Hristo Bojinov96be7202010-08-02 10:26:17 -0700440 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700441 for info in z.infolist():
442 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700443 basefilename = info.filename[7:]
444 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700445 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700446 out[fn] = common.File(fn, data)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700447 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
448 retouch_files.append(("/system/" + basefilename,
449 out[fn].sha1))
450 return (out, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700451
452
Doug Zongkereef39442009-04-02 12:14:19 -0700453def GetBuildProp(property, z):
454 """Return the fingerprint of the build of a given target-files
455 ZipFile object."""
456 bp = z.read("SYSTEM/build.prop")
457 if not property:
458 return bp
459 m = re.search(re.escape(property) + r"=(.*)\n", bp)
460 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700461 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700462 return m.group(1).strip()
463
464
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700465def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700466 source_version = OPTIONS.source_info_dict["recovery_api_version"]
467 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700468
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700469 if source_version == 0:
470 print ("WARNING: generating edify script for a source that "
471 "can't install it.")
Doug Zongkerb984ae52010-09-16 23:13:11 -0700472 script = edify_generator.EdifyGenerator(source_version, OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700473
Doug Zongker2ea21062010-04-28 16:05:21 -0700474 metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700475 "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700476 }
477
Doug Zongker05d3dea2009-06-22 11:32:31 -0700478 device_specific = common.DeviceSpecificParams(
479 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800480 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700481 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800482 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700483 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700484 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700485 metadata=metadata,
486 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700487
Doug Zongkereef39442009-04-02 12:14:19 -0700488 print "Loading target..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700489 (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700490 print "Loading source..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700491 (source_data, source_retouch_files) = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700492
493 verbatim_targets = []
494 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700495 diffs = []
Doug Zongkereef39442009-04-02 12:14:19 -0700496 largest_source_size = 0
497 for fn in sorted(target_data.keys()):
498 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700499 assert fn == tf.name
Doug Zongkereef39442009-04-02 12:14:19 -0700500 sf = source_data.get(fn, None)
501
502 if sf is None or fn in OPTIONS.require_verbatim:
503 # This file should be included verbatim
504 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700505 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700506 print "send", fn, "verbatim"
507 tf.AddToZip(output_zip)
508 verbatim_targets.append((fn, tf.size))
509 elif tf.sha1 != sf.sha1:
510 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700511 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700512 else:
513 # Target file identical to source.
514 pass
515
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700516 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700517
518 for diff in diffs:
519 tf, sf, d = diff.GetPatch()
520 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
521 # patch is almost as big as the file; don't bother patching
522 tf.AddToZip(output_zip)
523 verbatim_targets.append((tf.name, tf.size))
524 else:
525 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
Doug Zongker5a482092010-02-17 16:09:18 -0800526 patch_list.append((tf.name, tf, sf, tf.size, sha.sha(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700527 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700528
529 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
530 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700531 metadata["pre-build"] = source_fp
532 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700533
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700534 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700535 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700536
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700537 source_boot = common.File("/tmp/boot.img",
538 common.BuildBootableImage(
539 os.path.join(OPTIONS.source_tmp, "BOOT")))
540 target_boot = common.File("/tmp/boot.img",
541 common.BuildBootableImage(
542 os.path.join(OPTIONS.target_tmp, "BOOT")))
Doug Zongker5da317e2009-06-02 13:38:17 -0700543 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700544
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700545 source_recovery = common.File("system/recovery.img",
546 common.BuildBootableImage(
547 os.path.join(OPTIONS.source_tmp, "RECOVERY")))
548 target_recovery = common.File("system/recovery.img",
549 common.BuildBootableImage(
550 os.path.join(OPTIONS.target_tmp, "RECOVERY")))
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700551 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700552
Doug Zongker881dd402009-09-20 14:03:55 -0700553 # Here's how we divide up the progress bar:
554 # 0.1 for verifying the start state (PatchCheck calls)
555 # 0.8 for applying patches (ApplyPatch calls)
556 # 0.1 for unpacking verbatim files, symlinking, and doing the
557 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700558
559 AppendAssertions(script, target_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700560 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700561
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700562 script.Print("Verifying current system...")
563
Doug Zongker881dd402009-09-20 14:03:55 -0700564 script.ShowProgress(0.1, 0)
565 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
566 if updating_boot:
567 total_verify_size += source_boot.size
568 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700569
Doug Zongker5a482092010-02-17 16:09:18 -0800570 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700571 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700572 so_far += sf.size
573 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700574
Doug Zongker5da317e2009-06-02 13:38:17 -0700575 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700576 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700577 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700578 print "boot target: %d source: %d diff: %d" % (
579 target_boot.size, source_boot.size, len(d))
580
Doug Zongker048e7ca2009-06-15 14:31:53 -0700581 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700582
Doug Zongker96a57e72010-09-26 14:57:41 -0700583 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700584
585 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
586 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700587 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700588 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700589 so_far += source_boot.size
590 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700591
592 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700593 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800594
Doug Zongker05d3dea2009-06-22 11:32:31 -0700595 device_specific.IncrementalOTA_VerifyEnd()
596
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700597 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700598
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700599 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700600 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700601 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700602
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700603 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700604 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
605 ["/"+i for i in sorted(source_data)
Doug Zongker3b949f02009-08-24 10:24:32 -0700606 if i not in target_data] +
607 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700608
Doug Zongker881dd402009-09-20 14:03:55 -0700609 script.ShowProgress(0.8, 0)
610 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
611 if updating_boot:
612 total_patch_size += target_boot.size
613 so_far = 0
614
615 script.Print("Patching system files...")
Doug Zongker5a482092010-02-17 16:09:18 -0800616 for fn, tf, sf, size, _ in patch_list:
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800617 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700618 so_far += tf.size
619 script.SetProgress(so_far / total_patch_size)
620
Doug Zongkereef39442009-04-02 12:14:19 -0700621 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700622 # Produce the boot image by applying a patch to the current
623 # contents of the boot partition, and write it back to the
624 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700625 script.Print("Patching boot image...")
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700626 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
627 % (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700628 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700629 target_boot.size, target_boot.sha1),
630 "-",
631 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800632 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700633 so_far += target_boot.size
634 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700635 print "boot image changed; including."
636 else:
637 print "boot image unchanged; skipping."
638
639 if updating_recovery:
Doug Zongker73ef8252009-07-23 15:12:53 -0700640 # Is it better to generate recovery as a patch from the current
641 # boot image, or from the previous recovery image? For large
642 # updates with significant kernel changes, probably the former.
643 # For small updates where the kernel hasn't changed, almost
644 # certainly the latter. We pick the first option. Future
645 # complicated schemes may let us effectively use both.
646 #
647 # A wacky possibility: as long as there is room in the boot
648 # partition, include the binaries and image files from recovery in
649 # the boot image (though not in the ramdisk) so they can be used
650 # as fodder for constructing the recovery image.
Doug Zongker486de122010-09-16 14:01:56 -0700651 MakeRecoveryPatch(output_zip, target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800652 script.DeleteFiles(["/system/recovery-from-boot.p",
653 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700654 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700655 else:
656 print "recovery image unchanged; skipping."
657
Doug Zongker881dd402009-09-20 14:03:55 -0700658 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700659
Hristo Bojinov96be7202010-08-02 10:26:17 -0700660 (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700661
662 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700663 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700664 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700665 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700666
667 # Note that this call will mess up the tree of Items, so make sure
668 # we're done with it.
Hristo Bojinov96be7202010-08-02 10:26:17 -0700669 (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700670 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
671
672 # Delete all the symlinks in source that aren't in target. This
673 # needs to happen before verbatim files are unpacked, in case a
674 # symlink in the source is replaced by a real file in the target.
675 to_delete = []
676 for dest, link in source_symlinks:
677 if link not in target_symlinks_d:
678 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700679 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700680
681 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700682 script.Print("Unpacking new files...")
683 script.UnpackPackageDir("system", "/system")
684
Doug Zongker42265392010-02-12 10:21:00 -0800685 if updating_recovery:
686 script.Print("Unpacking new recovery...")
687 script.UnpackPackageDir("recovery", "/system")
688
Doug Zongker05d3dea2009-06-22 11:32:31 -0700689 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700690
691 # Create all the symlinks that don't already exist, or point to
692 # somewhere different than what we want. Delete each symlink before
693 # creating it, since the 'symlink' command won't overwrite.
694 to_create = []
695 for dest, link in target_symlinks:
696 if link in source_symlinks_d:
697 if dest != source_symlinks_d[link]:
698 to_create.append((dest, link))
699 else:
700 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700701 script.DeleteFiles([i[1] for i in to_create])
702 script.MakeSymlinks(to_create)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700703 if OPTIONS.aslr_mode:
704 script.RetouchBinaries(target_retouch_files)
705 else:
706 script.UndoRetouchBinaries(target_retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700707
708 # Now that the symlinks are created, we can set all the
709 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700710 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700711
Doug Zongker881dd402009-09-20 14:03:55 -0700712 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700713 device_specific.IncrementalOTA_InstallEnd()
714
Doug Zongker1c390a22009-05-14 19:06:36 -0700715 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700716 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700717
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700718 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700719 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700720
721
722def main(argv):
723
724 def option_handler(o, a):
725 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700726 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700727 elif o in ("-k", "--package_key"):
728 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700729 elif o in ("-i", "--incremental_from"):
730 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700731 elif o in ("-w", "--wipe_user_data"):
732 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700733 elif o in ("-n", "--no_prereq"):
734 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700735 elif o in ("-e", "--extra_script"):
736 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700737 elif o in ("-a", "--aslr_mode"):
738 if a in ("on", "On", "true", "True", "yes", "Yes"):
739 OPTIONS.aslr_mode = True
740 else:
741 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700742 elif o in ("--worker_threads"):
743 OPTIONS.worker_threads = int(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700744 else:
745 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700746 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700747
748 args = common.ParseOptions(argv, __doc__,
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700749 extra_opts="b:k:i:d:wne:a:",
Doug Zongkereef39442009-04-02 12:14:19 -0700750 extra_long_opts=["board_config=",
751 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700752 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700753 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700754 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700755 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700756 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700757 "aslr_mode=",
758 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700759 extra_option_handler=option_handler)
760
761 if len(args) != 2:
762 common.Usage(__doc__)
763 sys.exit(1)
764
Doug Zongker1c390a22009-05-14 19:06:36 -0700765 if OPTIONS.extra_script is not None:
766 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
767
Doug Zongkereef39442009-04-02 12:14:19 -0700768 print "unzipping target target-files..."
769 OPTIONS.input_tmp = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700770
Doug Zongkereef39442009-04-02 12:14:19 -0700771 OPTIONS.target_tmp = OPTIONS.input_tmp
772 input_zip = zipfile.ZipFile(args[0], "r")
Doug Zongker37974732010-09-16 17:44:38 -0700773 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
774 if OPTIONS.verbose:
775 print "--- target info ---"
776 common.DumpInfoDict(OPTIONS.info_dict)
777
778 if OPTIONS.device_specific is None:
779 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
780 if OPTIONS.device_specific is not None:
781 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
782 print "using device-specific extensions in", OPTIONS.device_specific
783
Doug Zongkereef39442009-04-02 12:14:19 -0700784 if OPTIONS.package_key:
785 temp_zip_file = tempfile.NamedTemporaryFile()
786 output_zip = zipfile.ZipFile(temp_zip_file, "w",
787 compression=zipfile.ZIP_DEFLATED)
788 else:
789 output_zip = zipfile.ZipFile(args[1], "w",
790 compression=zipfile.ZIP_DEFLATED)
791
792 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700793 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700794 else:
795 print "unzipping source target-files..."
796 OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
797 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
Doug Zongker37974732010-09-16 17:44:38 -0700798 OPTIONS.target_info_dict = OPTIONS.info_dict
799 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
800 if OPTIONS.verbose:
801 print "--- source info ---"
802 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700803 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700804
805 output_zip.close()
806 if OPTIONS.package_key:
807 SignOutput(temp_zip_file.name, args[1])
808 temp_zip_file.close()
809
810 common.Cleanup()
811
812 print "done."
813
814
815if __name__ == '__main__':
816 try:
817 main(sys.argv[1:])
818 except common.ExternalError, e:
819 print
820 print " ERROR: %s" % (e,)
821 print
822 sys.exit(1)