blob: 4264efa4b2cea161f28ca43744514d03d694a64f [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
davidcad0bb92011-03-15 14:21:38 +000066try:
67 from hashlib import sha1 as sha1
68except ImportError:
69 from sha import sha as sha1
70
Doug Zongkereef39442009-04-02 12:14:19 -070071import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070072import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070073
74OPTIONS = common.OPTIONS
75OPTIONS.package_key = "build/target/product/security/testkey"
76OPTIONS.incremental_source = None
77OPTIONS.require_verbatim = set()
78OPTIONS.prohibit_verbatim = set(("system/build.prop",))
79OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070080OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070081OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070082OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070083OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070084OPTIONS.worker_threads = 3
Doug Zongkereef39442009-04-02 12:14:19 -070085
86def 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 Zongkereef39442009-04-02 12:14:19 -0700105class Item:
106 """Items represent the metadata (user, group, mode) of files and
107 directories in the system image."""
108 ITEMS = {}
109 def __init__(self, name, dir=False):
110 self.name = name
111 self.uid = None
112 self.gid = None
113 self.mode = None
114 self.dir = dir
115
116 if name:
117 self.parent = Item.Get(os.path.dirname(name), dir=True)
118 self.parent.children.append(self)
119 else:
120 self.parent = None
121 if dir:
122 self.children = []
123
124 def Dump(self, indent=0):
125 if self.uid is not None:
126 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
127 else:
128 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
129 if self.dir:
130 print "%s%s" % (" "*indent, self.descendants)
131 print "%s%s" % (" "*indent, self.best_subtree)
132 for i in self.children:
133 i.Dump(indent=indent+1)
134
135 @classmethod
136 def Get(cls, name, dir=False):
137 if name not in cls.ITEMS:
138 cls.ITEMS[name] = Item(name, dir=dir)
139 return cls.ITEMS[name]
140
141 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700142 def GetMetadata(cls, input_zip):
143
144 try:
145 # See if the target_files contains a record of what the uid,
146 # gid, and mode is supposed to be.
147 output = input_zip.read("META/filesystem_config.txt")
148 except KeyError:
149 # Run the external 'fs_config' program to determine the desired
150 # uid, gid, and mode for every Item object. Note this uses the
151 # one in the client now, which might not be the same as the one
152 # used when this target_files was built.
153 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
154 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
155 suffix = { False: "", True: "/" }
156 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
157 for i in cls.ITEMS.itervalues() if i.name])
Doug Zongker3475d362010-03-17 16:39:30 -0700158 output, error = p.communicate(input)
Doug Zongker283e2a12010-03-15 17:52:32 -0700159 assert not error
Doug Zongkereef39442009-04-02 12:14:19 -0700160
161 for line in output.split("\n"):
162 if not line: continue
163 name, uid, gid, mode = line.split()
Doug Zongker283e2a12010-03-15 17:52:32 -0700164 i = cls.ITEMS.get(name, None)
165 if i is not None:
166 i.uid = int(uid)
167 i.gid = int(gid)
168 i.mode = int(mode, 8)
169 if i.dir:
170 i.children.sort(key=lambda i: i.name)
171
172 # set metadata for the files generated by this script.
173 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
174 if i: i.uid, i.gid, i.mode = 0, 0, 0644
175 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
176 if i: i.uid, i.gid, i.mode = 0, 0, 0544
Doug Zongkereef39442009-04-02 12:14:19 -0700177
178 def CountChildMetadata(self):
179 """Count up the (uid, gid, mode) tuples for all children and
180 determine the best strategy for using set_perm_recursive and
181 set_perm to correctly chown/chmod all the files to their desired
182 values. Recursively calls itself for all descendants.
183
184 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
185 all descendants of this node. (dmode or fmode may be None.) Also
186 sets the best_subtree of each directory Item to the (uid, gid,
187 dmode, fmode) tuple that will match the most descendants of that
188 Item.
189 """
190
191 assert self.dir
192 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
193 for i in self.children:
194 if i.dir:
195 for k, v in i.CountChildMetadata().iteritems():
196 d[k] = d.get(k, 0) + v
197 else:
198 k = (i.uid, i.gid, None, i.mode)
199 d[k] = d.get(k, 0) + 1
200
201 # Find the (uid, gid, dmode, fmode) tuple that matches the most
202 # descendants.
203
204 # First, find the (uid, gid) pair that matches the most
205 # descendants.
206 ug = {}
207 for (uid, gid, _, _), count in d.iteritems():
208 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
209 ug = MostPopularKey(ug, (0, 0))
210
211 # Now find the dmode and fmode that match the most descendants
212 # with that (uid, gid), and choose those.
213 best_dmode = (0, 0755)
214 best_fmode = (0, 0644)
215 for k, count in d.iteritems():
216 if k[:2] != ug: continue
217 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
218 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
219 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
220
221 return d
222
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700223 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700224 """Append set_perm/set_perm_recursive commands to 'script' to
225 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700226 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700227
228 self.CountChildMetadata()
229
230 def recurse(item, current):
231 # current is the (uid, gid, dmode, fmode) tuple that the current
232 # item (and all its children) have already been set to. We only
233 # need to issue set_perm/set_perm_recursive commands if we're
234 # supposed to be something different.
235 if item.dir:
236 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700237 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700238 current = item.best_subtree
239
240 if item.uid != current[0] or item.gid != current[1] or \
241 item.mode != current[2]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700242 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700243
244 for i in item.children:
245 recurse(i, current)
246 else:
247 if item.uid != current[0] or item.gid != current[1] or \
248 item.mode != current[3]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700249 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700250
251 recurse(self, (-1, -1, -1, -1))
252
253
254def CopySystemFiles(input_zip, output_zip=None,
255 substitute=None):
256 """Copies files underneath system/ in the input zip to the output
257 zip. Populates the Item class with their metadata, and returns a
Hristo Bojinov96be7202010-08-02 10:26:17 -0700258 list of symlinks as well as a list of files that will be retouched.
259 output_zip may be None, in which case the copy is skipped (but the
260 other side effects still happen). substitute is an optional dict
261 of {output filename: contents} to be output instead of certain input
262 files.
Doug Zongkereef39442009-04-02 12:14:19 -0700263 """
264
265 symlinks = []
Hristo Bojinov96be7202010-08-02 10:26:17 -0700266 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700267
268 for info in input_zip.infolist():
269 if info.filename.startswith("SYSTEM/"):
270 basefilename = info.filename[7:]
271 if IsSymlink(info):
272 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700273 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700274 else:
275 info2 = copy.copy(info)
276 fn = info2.filename = "system/" + basefilename
277 if substitute and fn in substitute and substitute[fn] is None:
278 continue
279 if output_zip is not None:
280 if substitute and fn in substitute:
281 data = substitute[fn]
282 else:
283 data = input_zip.read(info.filename)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700284 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
285 retouch_files.append(("/system/" + basefilename,
Doug Zongker55d93282011-01-25 17:03:34 -0800286 common.sha1(data).hexdigest()))
Doug Zongkereef39442009-04-02 12:14:19 -0700287 output_zip.writestr(info2, data)
288 if fn.endswith("/"):
289 Item.Get(fn[:-1], dir=True)
290 else:
291 Item.Get(fn, dir=False)
292
293 symlinks.sort()
Hristo Bojinov96be7202010-08-02 10:26:17 -0700294 return (symlinks, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700295
296
Doug Zongkereef39442009-04-02 12:14:19 -0700297def SignOutput(temp_zip_name, output_zip_name):
298 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
299 pw = key_passwords[OPTIONS.package_key]
300
Doug Zongker951495f2009-08-14 12:44:19 -0700301 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
302 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700303
304
Doug Zongkereef39442009-04-02 12:14:19 -0700305def AppendAssertions(script, input_zip):
Doug Zongkereef39442009-04-02 12:14:19 -0700306 device = GetBuildProp("ro.product.device", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700307 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700308
Doug Zongkereef39442009-04-02 12:14:19 -0700309
Doug Zongker486de122010-09-16 14:01:56 -0700310def MakeRecoveryPatch(output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700311 """Generate a binary patch that creates the recovery image starting
312 with the boot image. (Most of the space in these images is just the
313 kernel, which is identical for the two, so the resulting patch
314 should be efficient.) Add it to the output zip, along with a shell
315 script that is run from init.rc on first boot to actually do the
316 patching and install the new recovery image.
317
318 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700319 corresponding images. info should be the dictionary returned by
320 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700321
322 Returns an Item for the shell script, which must be made
323 executable.
324 """
325
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700326 d = common.Difference(recovery_img, boot_img)
Doug Zongker761e6422009-09-25 10:45:39 -0700327 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700328 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700329 Item.Get("system/recovery-from-boot.p", dir=False)
330
Doug Zongker96a57e72010-09-26 14:57:41 -0700331 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
332 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700333
Doug Zongker73ef8252009-07-23 15:12:53 -0700334 # Images with different content will have a different first page, so
335 # we check to see if this recovery has already been installed by
336 # testing just the first 2k.
337 HEADER_SIZE = 2048
Doug Zongker55d93282011-01-25 17:03:34 -0800338 header_sha1 = common.sha1(recovery_img.data[:HEADER_SIZE]).hexdigest()
Doug Zongker73ef8252009-07-23 15:12:53 -0700339 sh = """#!/system/bin/sh
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700340if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(header_size)d:%(header_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700341 log -t recovery "Installing new recovery image"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700342 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 -0700343else
344 log -t recovery "Recovery image already installed"
345fi
346""" % { 'boot_size': boot_img.size,
347 'boot_sha1': boot_img.sha1,
348 'header_size': HEADER_SIZE,
349 'header_sha1': header_sha1,
350 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700351 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700352 'boot_type': boot_type,
353 'boot_device': boot_device,
354 'recovery_type': recovery_type,
355 'recovery_device': recovery_device,
Doug Zongker67369982010-07-07 13:53:32 -0700356 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700357 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700358 return Item.Get("system/etc/install-recovery.sh", dir=False)
359
360
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700361def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700362 # TODO: how to determine this? We don't know what version it will
363 # be installed on top of. For now, we expect the API just won't
364 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700365 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700366
Doug Zongker2ea21062010-04-28 16:05:21 -0700367 metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
368 "pre-device": GetBuildProp("ro.product.device", input_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700369 "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700370 }
371
Doug Zongker05d3dea2009-06-22 11:32:31 -0700372 device_specific = common.DeviceSpecificParams(
373 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700374 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700375 output_zip=output_zip,
376 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700377 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700378 metadata=metadata,
379 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700380
Doug Zongker962069c2009-04-23 11:41:58 -0700381 if not OPTIONS.omit_prereq:
382 ts = GetBuildProp("ro.build.date.utc", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700383 script.AssertOlderBuild(ts)
Doug Zongkereef39442009-04-02 12:14:19 -0700384
385 AppendAssertions(script, input_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700386 device_specific.FullOTA_Assertions()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700387
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700388 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700389
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700390 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700391 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700392
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700393 script.FormatPartition("/system")
394 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700395 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700396 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700397
Hristo Bojinov96be7202010-08-02 10:26:17 -0700398 (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700399 script.MakeSymlinks(symlinks)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700400 if OPTIONS.aslr_mode:
401 script.RetouchBinaries(retouch_files)
402 else:
403 script.UndoRetouchBinaries(retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700404
Doug Zongker55d93282011-01-25 17:03:34 -0800405 boot_img = common.GetBootableImage("boot.img", "boot.img",
406 OPTIONS.input_tmp, "BOOT")
407 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
408 OPTIONS.input_tmp, "RECOVERY")
Doug Zongker486de122010-09-16 14:01:56 -0700409 MakeRecoveryPatch(output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700410
Doug Zongker283e2a12010-03-15 17:52:32 -0700411 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700412 Item.Get("system").SetPermissions(script)
413
Doug Zongker37974732010-09-16 17:44:38 -0700414 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700415 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700416 script.ShowProgress(0.2, 0)
417
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700418 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700419 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700420
421 script.ShowProgress(0.1, 0)
422 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700423
Doug Zongker1c390a22009-05-14 19:06:36 -0700424 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700425 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700426
Doug Zongker14833602010-02-02 13:12:04 -0800427 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700428 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700429 WriteMetadata(metadata, output_zip)
430
431
432def WriteMetadata(metadata, output_zip):
433 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
434 "".join(["%s=%s\n" % kv
435 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700436
437
Doug Zongkereef39442009-04-02 12:14:19 -0700438
439
440def LoadSystemFiles(z):
441 """Load all the files from SYSTEM/... in a given target-files
442 ZipFile, and return a dict of {filename: File object}."""
443 out = {}
Hristo Bojinov96be7202010-08-02 10:26:17 -0700444 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700445 for info in z.infolist():
446 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700447 basefilename = info.filename[7:]
448 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700449 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700450 out[fn] = common.File(fn, data)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700451 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
452 retouch_files.append(("/system/" + basefilename,
453 out[fn].sha1))
454 return (out, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700455
456
Doug Zongkereef39442009-04-02 12:14:19 -0700457def GetBuildProp(property, z):
458 """Return the fingerprint of the build of a given target-files
459 ZipFile object."""
460 bp = z.read("SYSTEM/build.prop")
461 if not property:
462 return bp
463 m = re.search(re.escape(property) + r"=(.*)\n", bp)
464 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700465 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700466 return m.group(1).strip()
467
468
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700469def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700470 source_version = OPTIONS.source_info_dict["recovery_api_version"]
471 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700472
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700473 if source_version == 0:
474 print ("WARNING: generating edify script for a source that "
475 "can't install it.")
Doug Zongkerb984ae52010-09-16 23:13:11 -0700476 script = edify_generator.EdifyGenerator(source_version, OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700477
Doug Zongker2ea21062010-04-28 16:05:21 -0700478 metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700479 "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700480 }
481
Doug Zongker05d3dea2009-06-22 11:32:31 -0700482 device_specific = common.DeviceSpecificParams(
483 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800484 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700485 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800486 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700487 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700488 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700489 metadata=metadata,
490 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700491
Doug Zongkereef39442009-04-02 12:14:19 -0700492 print "Loading target..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700493 (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700494 print "Loading source..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700495 (source_data, source_retouch_files) = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700496
497 verbatim_targets = []
498 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700499 diffs = []
Doug Zongkereef39442009-04-02 12:14:19 -0700500 largest_source_size = 0
501 for fn in sorted(target_data.keys()):
502 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700503 assert fn == tf.name
Doug Zongkereef39442009-04-02 12:14:19 -0700504 sf = source_data.get(fn, None)
505
506 if sf is None or fn in OPTIONS.require_verbatim:
507 # This file should be included verbatim
508 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700509 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700510 print "send", fn, "verbatim"
511 tf.AddToZip(output_zip)
512 verbatim_targets.append((fn, tf.size))
513 elif tf.sha1 != sf.sha1:
514 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700515 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700516 else:
517 # Target file identical to source.
518 pass
519
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700520 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700521
522 for diff in diffs:
523 tf, sf, d = diff.GetPatch()
524 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
525 # patch is almost as big as the file; don't bother patching
526 tf.AddToZip(output_zip)
527 verbatim_targets.append((tf.name, tf.size))
528 else:
529 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
Doug Zongker55d93282011-01-25 17:03:34 -0800530 patch_list.append((tf.name, tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700531 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700532
533 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
534 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700535 metadata["pre-build"] = source_fp
536 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700537
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700538 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700539 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700540
Doug Zongker55d93282011-01-25 17:03:34 -0800541 source_boot = common.GetBootableImage(
542 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT")
543 target_boot = common.GetBootableImage(
544 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker5da317e2009-06-02 13:38:17 -0700545 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700546
Doug Zongker55d93282011-01-25 17:03:34 -0800547 source_recovery = common.GetBootableImage(
548 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY")
549 target_recovery = common.GetBootableImage(
550 "/tmp/recovery.img", "recovery.img", 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 Zongkere92f15a2011-08-26 13:46:40 -0700616 deferred_patch_list = []
617 for item in patch_list:
618 fn, tf, sf, size, _ = item
619 if tf.name == "system/build.prop":
620 deferred_patch_list.append(item)
621 continue
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800622 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700623 so_far += tf.size
624 script.SetProgress(so_far / total_patch_size)
625
Doug Zongkereef39442009-04-02 12:14:19 -0700626 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700627 # Produce the boot image by applying a patch to the current
628 # contents of the boot partition, and write it back to the
629 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700630 script.Print("Patching boot image...")
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700631 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
632 % (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700633 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700634 target_boot.size, target_boot.sha1),
635 "-",
636 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800637 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700638 so_far += target_boot.size
639 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700640 print "boot image changed; including."
641 else:
642 print "boot image unchanged; skipping."
643
644 if updating_recovery:
Doug Zongker73ef8252009-07-23 15:12:53 -0700645 # Is it better to generate recovery as a patch from the current
646 # boot image, or from the previous recovery image? For large
647 # updates with significant kernel changes, probably the former.
648 # For small updates where the kernel hasn't changed, almost
649 # certainly the latter. We pick the first option. Future
650 # complicated schemes may let us effectively use both.
651 #
652 # A wacky possibility: as long as there is room in the boot
653 # partition, include the binaries and image files from recovery in
654 # the boot image (though not in the ramdisk) so they can be used
655 # as fodder for constructing the recovery image.
Doug Zongker486de122010-09-16 14:01:56 -0700656 MakeRecoveryPatch(output_zip, target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800657 script.DeleteFiles(["/system/recovery-from-boot.p",
658 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700659 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700660 else:
661 print "recovery image unchanged; skipping."
662
Doug Zongker881dd402009-09-20 14:03:55 -0700663 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700664
Hristo Bojinov96be7202010-08-02 10:26:17 -0700665 (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700666
667 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700668 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700669 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700670 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700671
672 # Note that this call will mess up the tree of Items, so make sure
673 # we're done with it.
Hristo Bojinov96be7202010-08-02 10:26:17 -0700674 (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700675 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
676
677 # Delete all the symlinks in source that aren't in target. This
678 # needs to happen before verbatim files are unpacked, in case a
679 # symlink in the source is replaced by a real file in the target.
680 to_delete = []
681 for dest, link in source_symlinks:
682 if link not in target_symlinks_d:
683 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700684 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700685
686 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700687 script.Print("Unpacking new files...")
688 script.UnpackPackageDir("system", "/system")
689
Doug Zongker42265392010-02-12 10:21:00 -0800690 if updating_recovery:
691 script.Print("Unpacking new recovery...")
692 script.UnpackPackageDir("recovery", "/system")
693
Doug Zongker05d3dea2009-06-22 11:32:31 -0700694 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700695
696 # Create all the symlinks that don't already exist, or point to
697 # somewhere different than what we want. Delete each symlink before
698 # creating it, since the 'symlink' command won't overwrite.
699 to_create = []
700 for dest, link in target_symlinks:
701 if link in source_symlinks_d:
702 if dest != source_symlinks_d[link]:
703 to_create.append((dest, link))
704 else:
705 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700706 script.DeleteFiles([i[1] for i in to_create])
707 script.MakeSymlinks(to_create)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700708 if OPTIONS.aslr_mode:
709 script.RetouchBinaries(target_retouch_files)
710 else:
711 script.UndoRetouchBinaries(target_retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700712
713 # Now that the symlinks are created, we can set all the
714 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700715 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700716
Doug Zongker881dd402009-09-20 14:03:55 -0700717 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700718 device_specific.IncrementalOTA_InstallEnd()
719
Doug Zongker1c390a22009-05-14 19:06:36 -0700720 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700721 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700722
Doug Zongkere92f15a2011-08-26 13:46:40 -0700723 # Patch the build.prop file last, so if something fails but the
724 # device can still come up, it appears to be the old build and will
725 # get set the OTA package again to retry.
726 script.Print("Patching remaining system files...")
727 for item in deferred_patch_list:
728 fn, tf, sf, size, _ = item
729 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
730 script.SetPermissions("/system/build.prop", 0, 0, 0644)
731
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700732 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700733 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700734
735
736def main(argv):
737
738 def option_handler(o, a):
739 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700740 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700741 elif o in ("-k", "--package_key"):
742 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700743 elif o in ("-i", "--incremental_from"):
744 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700745 elif o in ("-w", "--wipe_user_data"):
746 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700747 elif o in ("-n", "--no_prereq"):
748 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700749 elif o in ("-e", "--extra_script"):
750 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700751 elif o in ("-a", "--aslr_mode"):
752 if a in ("on", "On", "true", "True", "yes", "Yes"):
753 OPTIONS.aslr_mode = True
754 else:
755 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700756 elif o in ("--worker_threads"):
757 OPTIONS.worker_threads = int(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700758 else:
759 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700760 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700761
762 args = common.ParseOptions(argv, __doc__,
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700763 extra_opts="b:k:i:d:wne:a:",
Doug Zongkereef39442009-04-02 12:14:19 -0700764 extra_long_opts=["board_config=",
765 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700766 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700767 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700768 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700769 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700770 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700771 "aslr_mode=",
772 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700773 extra_option_handler=option_handler)
774
775 if len(args) != 2:
776 common.Usage(__doc__)
777 sys.exit(1)
778
Doug Zongker1c390a22009-05-14 19:06:36 -0700779 if OPTIONS.extra_script is not None:
780 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
781
Doug Zongkereef39442009-04-02 12:14:19 -0700782 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800783 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700784
Doug Zongkereef39442009-04-02 12:14:19 -0700785 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -0700786 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
787 if OPTIONS.verbose:
788 print "--- target info ---"
789 common.DumpInfoDict(OPTIONS.info_dict)
790
791 if OPTIONS.device_specific is None:
792 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
793 if OPTIONS.device_specific is not None:
794 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
795 print "using device-specific extensions in", OPTIONS.device_specific
796
Doug Zongkereef39442009-04-02 12:14:19 -0700797 if OPTIONS.package_key:
798 temp_zip_file = tempfile.NamedTemporaryFile()
799 output_zip = zipfile.ZipFile(temp_zip_file, "w",
800 compression=zipfile.ZIP_DEFLATED)
801 else:
802 output_zip = zipfile.ZipFile(args[1], "w",
803 compression=zipfile.ZIP_DEFLATED)
804
805 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700806 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700807 else:
808 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800809 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -0700810 OPTIONS.target_info_dict = OPTIONS.info_dict
811 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
812 if OPTIONS.verbose:
813 print "--- source info ---"
814 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700815 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700816
817 output_zip.close()
818 if OPTIONS.package_key:
819 SignOutput(temp_zip_file.name, args[1])
820 temp_zip_file.close()
821
822 common.Cleanup()
823
824 print "done."
825
826
827if __name__ == '__main__':
828 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -0800829 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -0700830 main(sys.argv[1:])
831 except common.ExternalError, e:
832 print
833 print " ERROR: %s" % (e,)
834 print
835 sys.exit(1)