blob: 2f650581c4121a5034c351d7b22390c329c6a5c6 [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
Doug Zongkerf2ab2902010-09-22 10:12:54 -070082# TODO: this is duplicated from edify_generator.py; fix.
83PARTITION_TYPES = { "yaffs2": "MTD", "mtd": "MTD",
84 "ext4": "EMMC", "emmc": "EMMC" }
85
Doug Zongkereef39442009-04-02 12:14:19 -070086def MostPopularKey(d, default):
87 """Given a dict, return the key corresponding to the largest
88 value. Returns 'default' if the dict is empty."""
89 x = [(v, k) for (k, v) in d.iteritems()]
90 if not x: return default
91 x.sort()
92 return x[-1][1]
93
94
95def IsSymlink(info):
96 """Return true if the zipfile.ZipInfo object passed in represents a
97 symlink."""
98 return (info.external_attr >> 16) == 0120777
99
Hristo Bojinov96be7202010-08-02 10:26:17 -0700100def IsRegular(info):
101 """Return true if the zipfile.ZipInfo object passed in represents a
102 symlink."""
103 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700104
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700105def GetTypeAndDevice(mount_point, info):
106 fstab = info["fstab"]
107 if fstab:
108 return PARTITION_TYPES[fstab[mount_point].fs_type], fstab[mount_point].device
109 else:
110 devices = {"/boot": "boot",
111 "/recovery": "recovery",
112 "/radio": "radio",
113 "/data": "userdata",
114 "/cache": "cache"}
115 return info["partition_type"], info.get("partition_path", "") + devices[mount_point]
116
Doug Zongkereef39442009-04-02 12:14:19 -0700117
118class Item:
119 """Items represent the metadata (user, group, mode) of files and
120 directories in the system image."""
121 ITEMS = {}
122 def __init__(self, name, dir=False):
123 self.name = name
124 self.uid = None
125 self.gid = None
126 self.mode = None
127 self.dir = dir
128
129 if name:
130 self.parent = Item.Get(os.path.dirname(name), dir=True)
131 self.parent.children.append(self)
132 else:
133 self.parent = None
134 if dir:
135 self.children = []
136
137 def Dump(self, indent=0):
138 if self.uid is not None:
139 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
140 else:
141 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
142 if self.dir:
143 print "%s%s" % (" "*indent, self.descendants)
144 print "%s%s" % (" "*indent, self.best_subtree)
145 for i in self.children:
146 i.Dump(indent=indent+1)
147
148 @classmethod
149 def Get(cls, name, dir=False):
150 if name not in cls.ITEMS:
151 cls.ITEMS[name] = Item(name, dir=dir)
152 return cls.ITEMS[name]
153
154 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700155 def GetMetadata(cls, input_zip):
156
157 try:
158 # See if the target_files contains a record of what the uid,
159 # gid, and mode is supposed to be.
160 output = input_zip.read("META/filesystem_config.txt")
161 except KeyError:
162 # Run the external 'fs_config' program to determine the desired
163 # uid, gid, and mode for every Item object. Note this uses the
164 # one in the client now, which might not be the same as the one
165 # used when this target_files was built.
166 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
167 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
168 suffix = { False: "", True: "/" }
169 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
170 for i in cls.ITEMS.itervalues() if i.name])
Doug Zongker3475d362010-03-17 16:39:30 -0700171 output, error = p.communicate(input)
Doug Zongker283e2a12010-03-15 17:52:32 -0700172 assert not error
Doug Zongkereef39442009-04-02 12:14:19 -0700173
174 for line in output.split("\n"):
175 if not line: continue
176 name, uid, gid, mode = line.split()
Doug Zongker283e2a12010-03-15 17:52:32 -0700177 i = cls.ITEMS.get(name, None)
178 if i is not None:
179 i.uid = int(uid)
180 i.gid = int(gid)
181 i.mode = int(mode, 8)
182 if i.dir:
183 i.children.sort(key=lambda i: i.name)
184
185 # set metadata for the files generated by this script.
186 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
187 if i: i.uid, i.gid, i.mode = 0, 0, 0644
188 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
189 if i: i.uid, i.gid, i.mode = 0, 0, 0544
Doug Zongkereef39442009-04-02 12:14:19 -0700190
191 def CountChildMetadata(self):
192 """Count up the (uid, gid, mode) tuples for all children and
193 determine the best strategy for using set_perm_recursive and
194 set_perm to correctly chown/chmod all the files to their desired
195 values. Recursively calls itself for all descendants.
196
197 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
198 all descendants of this node. (dmode or fmode may be None.) Also
199 sets the best_subtree of each directory Item to the (uid, gid,
200 dmode, fmode) tuple that will match the most descendants of that
201 Item.
202 """
203
204 assert self.dir
205 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
206 for i in self.children:
207 if i.dir:
208 for k, v in i.CountChildMetadata().iteritems():
209 d[k] = d.get(k, 0) + v
210 else:
211 k = (i.uid, i.gid, None, i.mode)
212 d[k] = d.get(k, 0) + 1
213
214 # Find the (uid, gid, dmode, fmode) tuple that matches the most
215 # descendants.
216
217 # First, find the (uid, gid) pair that matches the most
218 # descendants.
219 ug = {}
220 for (uid, gid, _, _), count in d.iteritems():
221 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
222 ug = MostPopularKey(ug, (0, 0))
223
224 # Now find the dmode and fmode that match the most descendants
225 # with that (uid, gid), and choose those.
226 best_dmode = (0, 0755)
227 best_fmode = (0, 0644)
228 for k, count in d.iteritems():
229 if k[:2] != ug: continue
230 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
231 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
232 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
233
234 return d
235
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700236 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700237 """Append set_perm/set_perm_recursive commands to 'script' to
238 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700239 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700240
241 self.CountChildMetadata()
242
243 def recurse(item, current):
244 # current is the (uid, gid, dmode, fmode) tuple that the current
245 # item (and all its children) have already been set to. We only
246 # need to issue set_perm/set_perm_recursive commands if we're
247 # supposed to be something different.
248 if item.dir:
249 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700250 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700251 current = item.best_subtree
252
253 if item.uid != current[0] or item.gid != current[1] or \
254 item.mode != current[2]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700255 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700256
257 for i in item.children:
258 recurse(i, current)
259 else:
260 if item.uid != current[0] or item.gid != current[1] or \
261 item.mode != current[3]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700262 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700263
264 recurse(self, (-1, -1, -1, -1))
265
266
267def CopySystemFiles(input_zip, output_zip=None,
268 substitute=None):
269 """Copies files underneath system/ in the input zip to the output
270 zip. Populates the Item class with their metadata, and returns a
Hristo Bojinov96be7202010-08-02 10:26:17 -0700271 list of symlinks as well as a list of files that will be retouched.
272 output_zip may be None, in which case the copy is skipped (but the
273 other side effects still happen). substitute is an optional dict
274 of {output filename: contents} to be output instead of certain input
275 files.
Doug Zongkereef39442009-04-02 12:14:19 -0700276 """
277
278 symlinks = []
Hristo Bojinov96be7202010-08-02 10:26:17 -0700279 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700280
281 for info in input_zip.infolist():
282 if info.filename.startswith("SYSTEM/"):
283 basefilename = info.filename[7:]
284 if IsSymlink(info):
285 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700286 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700287 else:
288 info2 = copy.copy(info)
289 fn = info2.filename = "system/" + basefilename
290 if substitute and fn in substitute and substitute[fn] is None:
291 continue
292 if output_zip is not None:
293 if substitute and fn in substitute:
294 data = substitute[fn]
295 else:
296 data = input_zip.read(info.filename)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700297 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
298 retouch_files.append(("/system/" + basefilename,
299 sha.sha(data).hexdigest()))
Doug Zongkereef39442009-04-02 12:14:19 -0700300 output_zip.writestr(info2, data)
301 if fn.endswith("/"):
302 Item.Get(fn[:-1], dir=True)
303 else:
304 Item.Get(fn, dir=False)
305
306 symlinks.sort()
Hristo Bojinov96be7202010-08-02 10:26:17 -0700307 return (symlinks, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700308
309
Doug Zongkereef39442009-04-02 12:14:19 -0700310def SignOutput(temp_zip_name, output_zip_name):
311 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
312 pw = key_passwords[OPTIONS.package_key]
313
Doug Zongker951495f2009-08-14 12:44:19 -0700314 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
315 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700316
317
Doug Zongkereef39442009-04-02 12:14:19 -0700318def AppendAssertions(script, input_zip):
Doug Zongkereef39442009-04-02 12:14:19 -0700319 device = GetBuildProp("ro.product.device", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700320 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700321
Doug Zongkereef39442009-04-02 12:14:19 -0700322
Doug Zongker486de122010-09-16 14:01:56 -0700323def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700324 """Generate a binary patch that creates the recovery image starting
325 with the boot image. (Most of the space in these images is just the
326 kernel, which is identical for the two, so the resulting patch
327 should be efficient.) Add it to the output zip, along with a shell
328 script that is run from init.rc on first boot to actually do the
329 patching and install the new recovery image.
330
331 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700332 corresponding images. info should be the dictionary returned by
333 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700334
335 Returns an Item for the shell script, which must be made
336 executable.
337 """
338
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700339 d = common.Difference(recovery_img, boot_img)
Doug Zongker761e6422009-09-25 10:45:39 -0700340 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700341 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700342 Item.Get("system/recovery-from-boot.p", dir=False)
343
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700344 boot_type, boot_device = GetTypeAndDevice("/boot", OPTIONS.info_dict)
345 recovery_type, recovery_device = GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700346
Doug Zongker73ef8252009-07-23 15:12:53 -0700347 # Images with different content will have a different first page, so
348 # we check to see if this recovery has already been installed by
349 # testing just the first 2k.
350 HEADER_SIZE = 2048
351 header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
352 sh = """#!/system/bin/sh
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700353if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(header_size)d:%(header_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700354 log -t recovery "Installing new recovery image"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700355 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 -0700356else
357 log -t recovery "Recovery image already installed"
358fi
359""" % { 'boot_size': boot_img.size,
360 'boot_sha1': boot_img.sha1,
361 'header_size': HEADER_SIZE,
362 'header_sha1': header_sha1,
363 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700364 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700365 'boot_type': boot_type,
366 'boot_device': boot_device,
367 'recovery_type': recovery_type,
368 'recovery_device': recovery_device,
Doug Zongker67369982010-07-07 13:53:32 -0700369 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700370 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700371 return Item.Get("system/etc/install-recovery.sh", dir=False)
372
373
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700374def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700375 # TODO: how to determine this? We don't know what version it will
376 # be installed on top of. For now, we expect the API just won't
377 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700378 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700379
Doug Zongker2ea21062010-04-28 16:05:21 -0700380 metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
381 "pre-device": GetBuildProp("ro.product.device", input_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700382 "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
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,
391 metadata=metadata)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700392
Doug Zongker962069c2009-04-23 11:41:58 -0700393 if not OPTIONS.omit_prereq:
394 ts = GetBuildProp("ro.build.date.utc", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700395 script.AssertOlderBuild(ts)
Doug Zongkereef39442009-04-02 12:14:19 -0700396
397 AppendAssertions(script, input_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700398 device_specific.FullOTA_Assertions()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700399
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700400 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700401
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700402 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700403 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700404
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700405 script.FormatPartition("/system")
406 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700407 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700408 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700409
Hristo Bojinov96be7202010-08-02 10:26:17 -0700410 (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700411 script.MakeSymlinks(symlinks)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700412 if OPTIONS.aslr_mode:
413 script.RetouchBinaries(retouch_files)
414 else:
415 script.UndoRetouchBinaries(retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700416
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700417 boot_img = common.File("boot.img", common.BuildBootableImage(
Doug Zongker73ef8252009-07-23 15:12:53 -0700418 os.path.join(OPTIONS.input_tmp, "BOOT")))
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700419 recovery_img = common.File("recovery.img", common.BuildBootableImage(
Doug Zongker73ef8252009-07-23 15:12:53 -0700420 os.path.join(OPTIONS.input_tmp, "RECOVERY")))
Doug Zongker486de122010-09-16 14:01:56 -0700421 MakeRecoveryPatch(output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700422
Doug Zongker283e2a12010-03-15 17:52:32 -0700423 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700424 Item.Get("system").SetPermissions(script)
425
Doug Zongker37974732010-09-16 17:44:38 -0700426 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700427 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700428 script.ShowProgress(0.2, 0)
429
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700430 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700431 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700432
433 script.ShowProgress(0.1, 0)
434 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700435
Doug Zongker1c390a22009-05-14 19:06:36 -0700436 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700437 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700438
Doug Zongker14833602010-02-02 13:12:04 -0800439 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700440 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700441 WriteMetadata(metadata, output_zip)
442
443
444def WriteMetadata(metadata, output_zip):
445 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
446 "".join(["%s=%s\n" % kv
447 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700448
449
Doug Zongkereef39442009-04-02 12:14:19 -0700450
451
452def LoadSystemFiles(z):
453 """Load all the files from SYSTEM/... in a given target-files
454 ZipFile, and return a dict of {filename: File object}."""
455 out = {}
Hristo Bojinov96be7202010-08-02 10:26:17 -0700456 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700457 for info in z.infolist():
458 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700459 basefilename = info.filename[7:]
460 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700461 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700462 out[fn] = common.File(fn, data)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700463 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
464 retouch_files.append(("/system/" + basefilename,
465 out[fn].sha1))
466 return (out, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700467
468
Doug Zongkereef39442009-04-02 12:14:19 -0700469def GetBuildProp(property, z):
470 """Return the fingerprint of the build of a given target-files
471 ZipFile object."""
472 bp = z.read("SYSTEM/build.prop")
473 if not property:
474 return bp
475 m = re.search(re.escape(property) + r"=(.*)\n", bp)
476 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700477 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700478 return m.group(1).strip()
479
480
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700481def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700482 source_version = OPTIONS.source_info_dict["recovery_api_version"]
483 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700484
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700485 if source_version == 0:
486 print ("WARNING: generating edify script for a source that "
487 "can't install it.")
Doug Zongkerb984ae52010-09-16 23:13:11 -0700488 script = edify_generator.EdifyGenerator(source_version, OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700489
Doug Zongker2ea21062010-04-28 16:05:21 -0700490 metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700491 "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700492 }
493
Doug Zongker05d3dea2009-06-22 11:32:31 -0700494 device_specific = common.DeviceSpecificParams(
495 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800496 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700497 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800498 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700499 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700500 script=script,
501 metadata=metadata)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700502
Doug Zongkereef39442009-04-02 12:14:19 -0700503 print "Loading target..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700504 (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700505 print "Loading source..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700506 (source_data, source_retouch_files) = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700507
508 verbatim_targets = []
509 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700510 diffs = []
Doug Zongkereef39442009-04-02 12:14:19 -0700511 largest_source_size = 0
512 for fn in sorted(target_data.keys()):
513 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700514 assert fn == tf.name
Doug Zongkereef39442009-04-02 12:14:19 -0700515 sf = source_data.get(fn, None)
516
517 if sf is None or fn in OPTIONS.require_verbatim:
518 # This file should be included verbatim
519 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700520 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700521 print "send", fn, "verbatim"
522 tf.AddToZip(output_zip)
523 verbatim_targets.append((fn, tf.size))
524 elif tf.sha1 != sf.sha1:
525 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700526 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700527 else:
528 # Target file identical to source.
529 pass
530
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700531 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700532
533 for diff in diffs:
534 tf, sf, d = diff.GetPatch()
535 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
536 # patch is almost as big as the file; don't bother patching
537 tf.AddToZip(output_zip)
538 verbatim_targets.append((tf.name, tf.size))
539 else:
540 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
Doug Zongker5a482092010-02-17 16:09:18 -0800541 patch_list.append((tf.name, tf, sf, tf.size, sha.sha(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700542 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700543
544 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
545 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700546 metadata["pre-build"] = source_fp
547 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700548
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700549 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700550 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700551
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700552 source_boot = common.File("/tmp/boot.img",
553 common.BuildBootableImage(
554 os.path.join(OPTIONS.source_tmp, "BOOT")))
555 target_boot = common.File("/tmp/boot.img",
556 common.BuildBootableImage(
557 os.path.join(OPTIONS.target_tmp, "BOOT")))
Doug Zongker5da317e2009-06-02 13:38:17 -0700558 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700559
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700560 source_recovery = common.File("system/recovery.img",
561 common.BuildBootableImage(
562 os.path.join(OPTIONS.source_tmp, "RECOVERY")))
563 target_recovery = common.File("system/recovery.img",
564 common.BuildBootableImage(
565 os.path.join(OPTIONS.target_tmp, "RECOVERY")))
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700566 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700567
Doug Zongker881dd402009-09-20 14:03:55 -0700568 # Here's how we divide up the progress bar:
569 # 0.1 for verifying the start state (PatchCheck calls)
570 # 0.8 for applying patches (ApplyPatch calls)
571 # 0.1 for unpacking verbatim files, symlinking, and doing the
572 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700573
574 AppendAssertions(script, target_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700575 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700576
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700577 script.Print("Verifying current system...")
578
Doug Zongker881dd402009-09-20 14:03:55 -0700579 script.ShowProgress(0.1, 0)
580 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
581 if updating_boot:
582 total_verify_size += source_boot.size
583 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700584
Doug Zongker5a482092010-02-17 16:09:18 -0800585 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700586 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700587 so_far += sf.size
588 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700589
Doug Zongker5da317e2009-06-02 13:38:17 -0700590 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700591 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700592 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700593 print "boot target: %d source: %d diff: %d" % (
594 target_boot.size, source_boot.size, len(d))
595
Doug Zongker048e7ca2009-06-15 14:31:53 -0700596 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700597
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700598 boot_type, boot_device = GetTypeAndDevice("/boot", OPTIONS.info_dict)
599
600 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
601 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700602 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700603 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700604 so_far += source_boot.size
605 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700606
607 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700608 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800609
Doug Zongker05d3dea2009-06-22 11:32:31 -0700610 device_specific.IncrementalOTA_VerifyEnd()
611
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700612 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700613
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700614 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700615 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700616 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700617
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700618 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700619 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
620 ["/"+i for i in sorted(source_data)
Doug Zongker3b949f02009-08-24 10:24:32 -0700621 if i not in target_data] +
622 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700623
Doug Zongker881dd402009-09-20 14:03:55 -0700624 script.ShowProgress(0.8, 0)
625 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
626 if updating_boot:
627 total_patch_size += target_boot.size
628 so_far = 0
629
630 script.Print("Patching system files...")
Doug Zongker5a482092010-02-17 16:09:18 -0800631 for fn, tf, sf, size, _ in patch_list:
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800632 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700633 so_far += tf.size
634 script.SetProgress(so_far / total_patch_size)
635
Doug Zongkereef39442009-04-02 12:14:19 -0700636 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700637 # Produce the boot image by applying a patch to the current
638 # contents of the boot partition, and write it back to the
639 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700640 script.Print("Patching boot image...")
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700641 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
642 % (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700643 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700644 target_boot.size, target_boot.sha1),
645 "-",
646 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800647 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700648 so_far += target_boot.size
649 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700650 print "boot image changed; including."
651 else:
652 print "boot image unchanged; skipping."
653
654 if updating_recovery:
Doug Zongker73ef8252009-07-23 15:12:53 -0700655 # Is it better to generate recovery as a patch from the current
656 # boot image, or from the previous recovery image? For large
657 # updates with significant kernel changes, probably the former.
658 # For small updates where the kernel hasn't changed, almost
659 # certainly the latter. We pick the first option. Future
660 # complicated schemes may let us effectively use both.
661 #
662 # A wacky possibility: as long as there is room in the boot
663 # partition, include the binaries and image files from recovery in
664 # the boot image (though not in the ramdisk) so they can be used
665 # as fodder for constructing the recovery image.
Doug Zongker486de122010-09-16 14:01:56 -0700666 MakeRecoveryPatch(output_zip, target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800667 script.DeleteFiles(["/system/recovery-from-boot.p",
668 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700669 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700670 else:
671 print "recovery image unchanged; skipping."
672
Doug Zongker881dd402009-09-20 14:03:55 -0700673 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700674
Hristo Bojinov96be7202010-08-02 10:26:17 -0700675 (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700676
677 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700678 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700679 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700680 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700681
682 # Note that this call will mess up the tree of Items, so make sure
683 # we're done with it.
Hristo Bojinov96be7202010-08-02 10:26:17 -0700684 (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700685 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
686
687 # Delete all the symlinks in source that aren't in target. This
688 # needs to happen before verbatim files are unpacked, in case a
689 # symlink in the source is replaced by a real file in the target.
690 to_delete = []
691 for dest, link in source_symlinks:
692 if link not in target_symlinks_d:
693 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700694 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700695
696 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700697 script.Print("Unpacking new files...")
698 script.UnpackPackageDir("system", "/system")
699
Doug Zongker42265392010-02-12 10:21:00 -0800700 if updating_recovery:
701 script.Print("Unpacking new recovery...")
702 script.UnpackPackageDir("recovery", "/system")
703
Doug Zongker05d3dea2009-06-22 11:32:31 -0700704 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700705
706 # Create all the symlinks that don't already exist, or point to
707 # somewhere different than what we want. Delete each symlink before
708 # creating it, since the 'symlink' command won't overwrite.
709 to_create = []
710 for dest, link in target_symlinks:
711 if link in source_symlinks_d:
712 if dest != source_symlinks_d[link]:
713 to_create.append((dest, link))
714 else:
715 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700716 script.DeleteFiles([i[1] for i in to_create])
717 script.MakeSymlinks(to_create)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700718 if OPTIONS.aslr_mode:
719 script.RetouchBinaries(target_retouch_files)
720 else:
721 script.UndoRetouchBinaries(target_retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700722
723 # Now that the symlinks are created, we can set all the
724 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700725 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700726
Doug Zongker881dd402009-09-20 14:03:55 -0700727 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700728 device_specific.IncrementalOTA_InstallEnd()
729
Doug Zongker1c390a22009-05-14 19:06:36 -0700730 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700731 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700732
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700733 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700734 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700735
736
737def main(argv):
738
739 def option_handler(o, a):
740 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700741 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700742 elif o in ("-k", "--package_key"):
743 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700744 elif o in ("-i", "--incremental_from"):
745 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700746 elif o in ("-w", "--wipe_user_data"):
747 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700748 elif o in ("-n", "--no_prereq"):
749 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700750 elif o in ("-e", "--extra_script"):
751 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700752 elif o in ("-a", "--aslr_mode"):
753 if a in ("on", "On", "true", "True", "yes", "Yes"):
754 OPTIONS.aslr_mode = True
755 else:
756 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700757 elif o in ("--worker_threads"):
758 OPTIONS.worker_threads = int(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700759 else:
760 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700761 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700762
763 args = common.ParseOptions(argv, __doc__,
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700764 extra_opts="b:k:i:d:wne:a:",
Doug Zongkereef39442009-04-02 12:14:19 -0700765 extra_long_opts=["board_config=",
766 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700767 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700768 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700769 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700770 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700771 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700772 "aslr_mode=",
773 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700774 extra_option_handler=option_handler)
775
776 if len(args) != 2:
777 common.Usage(__doc__)
778 sys.exit(1)
779
Doug Zongker1c390a22009-05-14 19:06:36 -0700780 if OPTIONS.extra_script is not None:
781 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
782
Doug Zongkereef39442009-04-02 12:14:19 -0700783 print "unzipping target target-files..."
784 OPTIONS.input_tmp = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700785
Doug Zongkereef39442009-04-02 12:14:19 -0700786 OPTIONS.target_tmp = OPTIONS.input_tmp
787 input_zip = zipfile.ZipFile(args[0], "r")
Doug Zongker37974732010-09-16 17:44:38 -0700788 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
789 if OPTIONS.verbose:
790 print "--- target info ---"
791 common.DumpInfoDict(OPTIONS.info_dict)
792
793 if OPTIONS.device_specific is None:
794 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
795 if OPTIONS.device_specific is not None:
796 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
797 print "using device-specific extensions in", OPTIONS.device_specific
798
Doug Zongkereef39442009-04-02 12:14:19 -0700799 if OPTIONS.package_key:
800 temp_zip_file = tempfile.NamedTemporaryFile()
801 output_zip = zipfile.ZipFile(temp_zip_file, "w",
802 compression=zipfile.ZIP_DEFLATED)
803 else:
804 output_zip = zipfile.ZipFile(args[1], "w",
805 compression=zipfile.ZIP_DEFLATED)
806
807 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700808 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700809 else:
810 print "unzipping source target-files..."
811 OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
812 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
Doug Zongker37974732010-09-16 17:44:38 -0700813 OPTIONS.target_info_dict = OPTIONS.info_dict
814 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
815 if OPTIONS.verbose:
816 print "--- source info ---"
817 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700818 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700819
820 output_zip.close()
821 if OPTIONS.package_key:
822 SignOutput(temp_zip_file.name, args[1])
823 temp_zip_file.close()
824
825 common.Cleanup()
826
827 print "done."
828
829
830if __name__ == '__main__':
831 try:
832 main(sys.argv[1:])
833 except common.ExternalError, e:
834 print
835 print " ERROR: %s" % (e,)
836 print
837 sys.exit(1)