blob: d24921476e6286a8b07357bdbf2ec2102c03a31c [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 Bojinov96be7202010-08-02 10:26:17 -070047 -a (--use_aslr)
48 Specify whether to build the package with ASLR enabled (off 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
Doug Zongker761e6422009-09-25 10:45:39 -070064import threading
Doug Zongkereef39442009-04-02 12:14:19 -070065import time
66import zipfile
67
68import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070069import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070070
71OPTIONS = common.OPTIONS
72OPTIONS.package_key = "build/target/product/security/testkey"
73OPTIONS.incremental_source = None
74OPTIONS.require_verbatim = set()
75OPTIONS.prohibit_verbatim = set(("system/build.prop",))
76OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070077OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070078OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070079OPTIONS.extra_script = None
Hristo Bojinov96be7202010-08-02 10:26:17 -070080OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -070081OPTIONS.worker_threads = 3
Doug Zongkereef39442009-04-02 12:14:19 -070082
83def MostPopularKey(d, default):
84 """Given a dict, return the key corresponding to the largest
85 value. Returns 'default' if the dict is empty."""
86 x = [(v, k) for (k, v) in d.iteritems()]
87 if not x: return default
88 x.sort()
89 return x[-1][1]
90
91
92def IsSymlink(info):
93 """Return true if the zipfile.ZipInfo object passed in represents a
94 symlink."""
95 return (info.external_attr >> 16) == 0120777
96
Hristo Bojinov96be7202010-08-02 10:26:17 -070097def IsRegular(info):
98 """Return true if the zipfile.ZipInfo object passed in represents a
99 symlink."""
100 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700101
102
103class Item:
104 """Items represent the metadata (user, group, mode) of files and
105 directories in the system image."""
106 ITEMS = {}
107 def __init__(self, name, dir=False):
108 self.name = name
109 self.uid = None
110 self.gid = None
111 self.mode = None
112 self.dir = dir
113
114 if name:
115 self.parent = Item.Get(os.path.dirname(name), dir=True)
116 self.parent.children.append(self)
117 else:
118 self.parent = None
119 if dir:
120 self.children = []
121
122 def Dump(self, indent=0):
123 if self.uid is not None:
124 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
125 else:
126 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
127 if self.dir:
128 print "%s%s" % (" "*indent, self.descendants)
129 print "%s%s" % (" "*indent, self.best_subtree)
130 for i in self.children:
131 i.Dump(indent=indent+1)
132
133 @classmethod
134 def Get(cls, name, dir=False):
135 if name not in cls.ITEMS:
136 cls.ITEMS[name] = Item(name, dir=dir)
137 return cls.ITEMS[name]
138
139 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700140 def GetMetadata(cls, input_zip):
141
142 try:
143 # See if the target_files contains a record of what the uid,
144 # gid, and mode is supposed to be.
145 output = input_zip.read("META/filesystem_config.txt")
146 except KeyError:
147 # Run the external 'fs_config' program to determine the desired
148 # uid, gid, and mode for every Item object. Note this uses the
149 # one in the client now, which might not be the same as the one
150 # used when this target_files was built.
151 p = common.Run(["fs_config"], stdin=subprocess.PIPE,
152 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
153 suffix = { False: "", True: "/" }
154 input = "".join(["%s%s\n" % (i.name, suffix[i.dir])
155 for i in cls.ITEMS.itervalues() if i.name])
Doug Zongker3475d362010-03-17 16:39:30 -0700156 output, error = p.communicate(input)
Doug Zongker283e2a12010-03-15 17:52:32 -0700157 assert not error
Doug Zongkereef39442009-04-02 12:14:19 -0700158
159 for line in output.split("\n"):
160 if not line: continue
161 name, uid, gid, mode = line.split()
Doug Zongker283e2a12010-03-15 17:52:32 -0700162 i = cls.ITEMS.get(name, None)
163 if i is not None:
164 i.uid = int(uid)
165 i.gid = int(gid)
166 i.mode = int(mode, 8)
167 if i.dir:
168 i.children.sort(key=lambda i: i.name)
169
170 # set metadata for the files generated by this script.
171 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
172 if i: i.uid, i.gid, i.mode = 0, 0, 0644
173 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
174 if i: i.uid, i.gid, i.mode = 0, 0, 0544
Doug Zongkereef39442009-04-02 12:14:19 -0700175
176 def CountChildMetadata(self):
177 """Count up the (uid, gid, mode) tuples for all children and
178 determine the best strategy for using set_perm_recursive and
179 set_perm to correctly chown/chmod all the files to their desired
180 values. Recursively calls itself for all descendants.
181
182 Returns a dict of {(uid, gid, dmode, fmode): count} counting up
183 all descendants of this node. (dmode or fmode may be None.) Also
184 sets the best_subtree of each directory Item to the (uid, gid,
185 dmode, fmode) tuple that will match the most descendants of that
186 Item.
187 """
188
189 assert self.dir
190 d = self.descendants = {(self.uid, self.gid, self.mode, None): 1}
191 for i in self.children:
192 if i.dir:
193 for k, v in i.CountChildMetadata().iteritems():
194 d[k] = d.get(k, 0) + v
195 else:
196 k = (i.uid, i.gid, None, i.mode)
197 d[k] = d.get(k, 0) + 1
198
199 # Find the (uid, gid, dmode, fmode) tuple that matches the most
200 # descendants.
201
202 # First, find the (uid, gid) pair that matches the most
203 # descendants.
204 ug = {}
205 for (uid, gid, _, _), count in d.iteritems():
206 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
207 ug = MostPopularKey(ug, (0, 0))
208
209 # Now find the dmode and fmode that match the most descendants
210 # with that (uid, gid), and choose those.
211 best_dmode = (0, 0755)
212 best_fmode = (0, 0644)
213 for k, count in d.iteritems():
214 if k[:2] != ug: continue
215 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
216 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
217 self.best_subtree = ug + (best_dmode[1], best_fmode[1])
218
219 return d
220
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700221 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700222 """Append set_perm/set_perm_recursive commands to 'script' to
223 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700224 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700225
226 self.CountChildMetadata()
227
228 def recurse(item, current):
229 # current is the (uid, gid, dmode, fmode) tuple that the current
230 # item (and all its children) have already been set to. We only
231 # need to issue set_perm/set_perm_recursive commands if we're
232 # supposed to be something different.
233 if item.dir:
234 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700235 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700236 current = item.best_subtree
237
238 if item.uid != current[0] or item.gid != current[1] or \
239 item.mode != current[2]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700240 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700241
242 for i in item.children:
243 recurse(i, current)
244 else:
245 if item.uid != current[0] or item.gid != current[1] or \
246 item.mode != current[3]:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700247 script.SetPermissions("/"+item.name, item.uid, item.gid, item.mode)
Doug Zongkereef39442009-04-02 12:14:19 -0700248
249 recurse(self, (-1, -1, -1, -1))
250
251
252def CopySystemFiles(input_zip, output_zip=None,
253 substitute=None):
254 """Copies files underneath system/ in the input zip to the output
255 zip. Populates the Item class with their metadata, and returns a
Hristo Bojinov96be7202010-08-02 10:26:17 -0700256 list of symlinks as well as a list of files that will be retouched.
257 output_zip may be None, in which case the copy is skipped (but the
258 other side effects still happen). substitute is an optional dict
259 of {output filename: contents} to be output instead of certain input
260 files.
Doug Zongkereef39442009-04-02 12:14:19 -0700261 """
262
263 symlinks = []
Hristo Bojinov96be7202010-08-02 10:26:17 -0700264 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700265
266 for info in input_zip.infolist():
267 if info.filename.startswith("SYSTEM/"):
268 basefilename = info.filename[7:]
269 if IsSymlink(info):
270 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700271 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700272 else:
273 info2 = copy.copy(info)
274 fn = info2.filename = "system/" + basefilename
275 if substitute and fn in substitute and substitute[fn] is None:
276 continue
277 if output_zip is not None:
278 if substitute and fn in substitute:
279 data = substitute[fn]
280 else:
281 data = input_zip.read(info.filename)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700282 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
283 retouch_files.append(("/system/" + basefilename,
284 sha.sha(data).hexdigest()))
Doug Zongkereef39442009-04-02 12:14:19 -0700285 output_zip.writestr(info2, data)
286 if fn.endswith("/"):
287 Item.Get(fn[:-1], dir=True)
288 else:
289 Item.Get(fn, dir=False)
290
291 symlinks.sort()
Hristo Bojinov96be7202010-08-02 10:26:17 -0700292 return (symlinks, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700293
294
Doug Zongkereef39442009-04-02 12:14:19 -0700295def SignOutput(temp_zip_name, output_zip_name):
296 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
297 pw = key_passwords[OPTIONS.package_key]
298
Doug Zongker951495f2009-08-14 12:44:19 -0700299 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
300 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700301
302
Doug Zongkereef39442009-04-02 12:14:19 -0700303def AppendAssertions(script, input_zip):
Doug Zongkereef39442009-04-02 12:14:19 -0700304 device = GetBuildProp("ro.product.device", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700305 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700306
Doug Zongkereef39442009-04-02 12:14:19 -0700307
Doug Zongker67369982010-07-07 13:53:32 -0700308def MakeRecoveryPatch(output_zip, recovery_img, boot_img, info):
Doug Zongker73ef8252009-07-23 15:12:53 -0700309 """Generate a binary patch that creates the recovery image starting
310 with the boot image. (Most of the space in these images is just the
311 kernel, which is identical for the two, so the resulting patch
312 should be efficient.) Add it to the output zip, along with a shell
313 script that is run from init.rc on first boot to actually do the
314 patching and install the new recovery image.
315
316 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700317 corresponding images. info should be the dictionary returned by
318 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700319
320 Returns an Item for the shell script, which must be made
321 executable.
322 """
323
Doug Zongker761e6422009-09-25 10:45:39 -0700324 d = Difference(recovery_img, boot_img)
325 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700326 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700327 Item.Get("system/recovery-from-boot.p", dir=False)
328
329 # Images with different content will have a different first page, so
330 # we check to see if this recovery has already been installed by
331 # testing just the first 2k.
332 HEADER_SIZE = 2048
333 header_sha1 = sha.sha(recovery_img.data[:HEADER_SIZE]).hexdigest()
334 sh = """#!/system/bin/sh
Doug Zongker67369982010-07-07 13:53:32 -0700335if ! applypatch -c %(partition_type)s:%(partition_path)srecovery:%(header_size)d:%(header_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700336 log -t recovery "Installing new recovery image"
Doug Zongker67369982010-07-07 13:53:32 -0700337 applypatch %(partition_type)s:%(partition_path)sboot:%(boot_size)d:%(boot_sha1)s %(partition_type)s:%(partition_path)srecovery %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p
Doug Zongker73ef8252009-07-23 15:12:53 -0700338else
339 log -t recovery "Recovery image already installed"
340fi
341""" % { 'boot_size': boot_img.size,
342 'boot_sha1': boot_img.sha1,
343 'header_size': HEADER_SIZE,
344 'header_sha1': header_sha1,
345 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700346 'recovery_sha1': recovery_img.sha1,
347 'partition_type': info["partition_type"],
348 'partition_path': info.get("partition_path", ""),
349 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700350 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700351 return Item.Get("system/etc/install-recovery.sh", dir=False)
352
353
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700354def WriteFullOTAPackage(input_zip, output_zip, info):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700355 # TODO: how to determine this? We don't know what version it will
356 # be installed on top of. For now, we expect the API just won't
357 # change very often.
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700358 script = edify_generator.EdifyGenerator(3, info)
Doug Zongkereef39442009-04-02 12:14:19 -0700359
Doug Zongker2ea21062010-04-28 16:05:21 -0700360 metadata = {"post-build": GetBuildProp("ro.build.fingerprint", input_zip),
361 "pre-device": GetBuildProp("ro.product.device", input_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700362 "post-timestamp": GetBuildProp("ro.build.date.utc", input_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700363 }
364
Doug Zongker05d3dea2009-06-22 11:32:31 -0700365 device_specific = common.DeviceSpecificParams(
366 input_zip=input_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800367 input_version=GetRecoveryAPIVersion(input_zip),
Doug Zongker05d3dea2009-06-22 11:32:31 -0700368 output_zip=output_zip,
369 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700370 input_tmp=OPTIONS.input_tmp,
371 metadata=metadata)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700372
Doug Zongker962069c2009-04-23 11:41:58 -0700373 if not OPTIONS.omit_prereq:
374 ts = GetBuildProp("ro.build.date.utc", input_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700375 script.AssertOlderBuild(ts)
Doug Zongkereef39442009-04-02 12:14:19 -0700376
377 AppendAssertions(script, input_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700378 device_specific.FullOTA_Assertions()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700379
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700380 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700381
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700382 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700383 script.FormatPartition("userdata")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700384
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700385 script.FormatPartition("system")
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700386 script.Mount("system", "/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700387 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700388 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700389
Hristo Bojinov96be7202010-08-02 10:26:17 -0700390 (symlinks, retouch_files) = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700391 script.MakeSymlinks(symlinks)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700392 if OPTIONS.aslr_mode:
393 script.RetouchBinaries(retouch_files)
394 else:
395 script.UndoRetouchBinaries(retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700396
Doug Zongker73ef8252009-07-23 15:12:53 -0700397 boot_img = File("boot.img", common.BuildBootableImage(
398 os.path.join(OPTIONS.input_tmp, "BOOT")))
399 recovery_img = File("recovery.img", common.BuildBootableImage(
400 os.path.join(OPTIONS.input_tmp, "RECOVERY")))
Doug Zongker67369982010-07-07 13:53:32 -0700401 MakeRecoveryPatch(output_zip, recovery_img, boot_img, info)
Doug Zongkereef39442009-04-02 12:14:19 -0700402
Doug Zongker283e2a12010-03-15 17:52:32 -0700403 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700404 Item.Get("system").SetPermissions(script)
405
406 common.CheckSize(boot_img.data, "boot.img")
407 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700408 script.ShowProgress(0.2, 0)
409
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700410 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700411 script.WriteRawImage("boot", "boot.img")
412
413 script.ShowProgress(0.1, 0)
414 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700415
Doug Zongker1c390a22009-05-14 19:06:36 -0700416 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700417 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700418
Doug Zongker14833602010-02-02 13:12:04 -0800419 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700420 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700421 WriteMetadata(metadata, output_zip)
422
423
424def WriteMetadata(metadata, output_zip):
425 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
426 "".join(["%s=%s\n" % kv
427 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700428
429
430class File(object):
431 def __init__(self, name, data):
432 self.name = name
433 self.data = data
434 self.size = len(data)
435 self.sha1 = sha.sha(data).hexdigest()
436
437 def WriteToTemp(self):
438 t = tempfile.NamedTemporaryFile()
439 t.write(self.data)
440 t.flush()
441 return t
442
443 def AddToZip(self, z):
Doug Zongker048e7ca2009-06-15 14:31:53 -0700444 common.ZipWriteStr(z, self.name, self.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700445
446
447def LoadSystemFiles(z):
448 """Load all the files from SYSTEM/... in a given target-files
449 ZipFile, and return a dict of {filename: File object}."""
450 out = {}
Hristo Bojinov96be7202010-08-02 10:26:17 -0700451 retouch_files = []
Doug Zongkereef39442009-04-02 12:14:19 -0700452 for info in z.infolist():
453 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700454 basefilename = info.filename[7:]
455 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700456 data = z.read(info.filename)
457 out[fn] = File(fn, data)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700458 if info.filename.startswith("SYSTEM/lib/") and IsRegular(info):
459 retouch_files.append(("/system/" + basefilename,
460 out[fn].sha1))
461 return (out, retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700462
463
Doug Zongker761e6422009-09-25 10:45:39 -0700464DIFF_PROGRAM_BY_EXT = {
465 ".gz" : "imgdiff",
466 ".zip" : ["imgdiff", "-z"],
467 ".jar" : ["imgdiff", "-z"],
468 ".apk" : ["imgdiff", "-z"],
469 ".img" : "imgdiff",
470 }
Doug Zongkereef39442009-04-02 12:14:19 -0700471
Doug Zongkereef39442009-04-02 12:14:19 -0700472
Doug Zongker761e6422009-09-25 10:45:39 -0700473class Difference(object):
474 def __init__(self, tf, sf):
475 self.tf = tf
476 self.sf = sf
477 self.patch = None
Doug Zongkereef39442009-04-02 12:14:19 -0700478
Doug Zongker761e6422009-09-25 10:45:39 -0700479 def ComputePatch(self):
480 """Compute the patch (as a string of data) needed to turn sf into
481 tf. Returns the same tuple as GetPatch()."""
Doug Zongkereef39442009-04-02 12:14:19 -0700482
Doug Zongker761e6422009-09-25 10:45:39 -0700483 tf = self.tf
484 sf = self.sf
485
486 ext = os.path.splitext(tf.name)[1]
487 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
488
489 ttemp = tf.WriteToTemp()
490 stemp = sf.WriteToTemp()
491
492 ext = os.path.splitext(tf.name)[1]
493
494 try:
495 ptemp = tempfile.NamedTemporaryFile()
496 if isinstance(diff_program, list):
497 cmd = copy.copy(diff_program)
498 else:
499 cmd = [diff_program]
500 cmd.append(stemp.name)
501 cmd.append(ttemp.name)
502 cmd.append(ptemp.name)
503 p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
504 _, err = p.communicate()
505 if err or p.returncode != 0:
506 print "WARNING: failure running %s:\n%s\n" % (diff_program, err)
507 return None
508 diff = ptemp.read()
509 finally:
510 ptemp.close()
511 stemp.close()
512 ttemp.close()
513
514 self.patch = diff
515 return self.tf, self.sf, self.patch
516
517
518 def GetPatch(self):
519 """Return a tuple (target_file, source_file, patch_data).
520 patch_data may be None if ComputePatch hasn't been called, or if
521 computing the patch failed."""
522 return self.tf, self.sf, self.patch
523
524
525def ComputeDifferences(diffs):
526 """Call ComputePatch on all the Difference objects in 'diffs'."""
527 print len(diffs), "diffs to compute"
528
529 # Do the largest files first, to try and reduce the long-pole effect.
530 by_size = [(i.tf.size, i) for i in diffs]
531 by_size.sort(reverse=True)
532 by_size = [i[1] for i in by_size]
533
534 lock = threading.Lock()
535 diff_iter = iter(by_size) # accessed under lock
536
537 def worker():
538 try:
539 lock.acquire()
540 for d in diff_iter:
541 lock.release()
542 start = time.time()
543 d.ComputePatch()
544 dur = time.time() - start
545 lock.acquire()
546
547 tf, sf, patch = d.GetPatch()
548 if sf.name == tf.name:
549 name = tf.name
550 else:
551 name = "%s (%s)" % (tf.name, sf.name)
552 if patch is None:
553 print "patching failed! %s" % (name,)
554 else:
555 print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
556 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)
557 lock.release()
Doug Zongker481c4e62009-09-28 10:07:13 -0700558 except Exception, e:
Doug Zongker761e6422009-09-25 10:45:39 -0700559 print e
560 raise
561
562 # start worker threads; wait for them all to finish.
563 threads = [threading.Thread(target=worker)
564 for i in range(OPTIONS.worker_threads)]
565 for th in threads:
566 th.start()
567 while threads:
568 threads.pop().join()
Doug Zongkereef39442009-04-02 12:14:19 -0700569
570
571def GetBuildProp(property, z):
572 """Return the fingerprint of the build of a given target-files
573 ZipFile object."""
574 bp = z.read("SYSTEM/build.prop")
575 if not property:
576 return bp
577 m = re.search(re.escape(property) + r"=(.*)\n", bp)
578 if not m:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700579 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700580 return m.group(1).strip()
581
582
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700583def GetRecoveryAPIVersion(zip):
584 """Returns the version of the recovery API. Version 0 is the older
585 amend code (no separate binary)."""
586 try:
587 version = zip.read("META/recovery-api-version.txt")
588 return int(version)
589 except KeyError:
590 try:
591 # version one didn't have the recovery-api-version.txt file, but
592 # it did include an updater binary.
593 zip.getinfo("OTA/bin/updater")
594 return 1
595 except KeyError:
596 return 0
597
Doug Zongker15604b82009-09-01 17:53:34 -0700598
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700599def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip, info):
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700600 source_version = GetRecoveryAPIVersion(source_zip)
Doug Zongker14833602010-02-02 13:12:04 -0800601 target_version = GetRecoveryAPIVersion(target_zip)
Doug Zongker67369982010-07-07 13:53:32 -0700602 partition_type = info["partition_type"]
603 partition_path = info.get("partition_path", "")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700604
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700605 if source_version == 0:
606 print ("WARNING: generating edify script for a source that "
607 "can't install it.")
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700608 script = edify_generator.EdifyGenerator(source_version, info)
Doug Zongkereef39442009-04-02 12:14:19 -0700609
Doug Zongker2ea21062010-04-28 16:05:21 -0700610 metadata = {"pre-device": GetBuildProp("ro.product.device", source_zip),
Doug Zongker3b852692010-06-21 15:30:45 -0700611 "post-timestamp": GetBuildProp("ro.build.date.utc", target_zip),
Doug Zongker2ea21062010-04-28 16:05:21 -0700612 }
613
Doug Zongker05d3dea2009-06-22 11:32:31 -0700614 device_specific = common.DeviceSpecificParams(
615 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800616 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700617 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800618 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700619 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700620 script=script,
621 metadata=metadata)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700622
Doug Zongkereef39442009-04-02 12:14:19 -0700623 print "Loading target..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700624 (target_data, target_retouch_files) = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700625 print "Loading source..."
Hristo Bojinov96be7202010-08-02 10:26:17 -0700626 (source_data, source_retouch_files) = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700627
628 verbatim_targets = []
629 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700630 diffs = []
Doug Zongkereef39442009-04-02 12:14:19 -0700631 largest_source_size = 0
632 for fn in sorted(target_data.keys()):
633 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700634 assert fn == tf.name
Doug Zongkereef39442009-04-02 12:14:19 -0700635 sf = source_data.get(fn, None)
636
637 if sf is None or fn in OPTIONS.require_verbatim:
638 # This file should be included verbatim
639 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700640 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700641 print "send", fn, "verbatim"
642 tf.AddToZip(output_zip)
643 verbatim_targets.append((fn, tf.size))
644 elif tf.sha1 != sf.sha1:
645 # File is different; consider sending as a patch
Doug Zongker761e6422009-09-25 10:45:39 -0700646 diffs.append(Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700647 else:
648 # Target file identical to source.
649 pass
650
Doug Zongker761e6422009-09-25 10:45:39 -0700651 ComputeDifferences(diffs)
652
653 for diff in diffs:
654 tf, sf, d = diff.GetPatch()
655 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
656 # patch is almost as big as the file; don't bother patching
657 tf.AddToZip(output_zip)
658 verbatim_targets.append((tf.name, tf.size))
659 else:
660 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
Doug Zongker5a482092010-02-17 16:09:18 -0800661 patch_list.append((tf.name, tf, sf, tf.size, sha.sha(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700662 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700663
664 source_fp = GetBuildProp("ro.build.fingerprint", source_zip)
665 target_fp = GetBuildProp("ro.build.fingerprint", target_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700666 metadata["pre-build"] = source_fp
667 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700668
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700669 script.Mount("system", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700670 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700671
Doug Zongker5da317e2009-06-02 13:38:17 -0700672 source_boot = File("/tmp/boot.img",
673 common.BuildBootableImage(
674 os.path.join(OPTIONS.source_tmp, "BOOT")))
675 target_boot = File("/tmp/boot.img",
676 common.BuildBootableImage(
677 os.path.join(OPTIONS.target_tmp, "BOOT")))
678 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700679
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700680 source_recovery = File("system/recovery.img",
681 common.BuildBootableImage(
682 os.path.join(OPTIONS.source_tmp, "RECOVERY")))
683 target_recovery = File("system/recovery.img",
684 common.BuildBootableImage(
685 os.path.join(OPTIONS.target_tmp, "RECOVERY")))
686 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700687
Doug Zongker881dd402009-09-20 14:03:55 -0700688 # Here's how we divide up the progress bar:
689 # 0.1 for verifying the start state (PatchCheck calls)
690 # 0.8 for applying patches (ApplyPatch calls)
691 # 0.1 for unpacking verbatim files, symlinking, and doing the
692 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700693
694 AppendAssertions(script, target_zip)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700695 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700696
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700697 script.Print("Verifying current system...")
698
Doug Zongker881dd402009-09-20 14:03:55 -0700699 script.ShowProgress(0.1, 0)
700 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
701 if updating_boot:
702 total_verify_size += source_boot.size
703 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700704
Doug Zongker5a482092010-02-17 16:09:18 -0800705 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700706 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700707 so_far += sf.size
708 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700709
Doug Zongker5da317e2009-06-02 13:38:17 -0700710 if updating_boot:
Doug Zongker761e6422009-09-25 10:45:39 -0700711 d = Difference(target_boot, source_boot)
712 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700713 print "boot target: %d source: %d diff: %d" % (
714 target_boot.size, source_boot.size, len(d))
715
Doug Zongker048e7ca2009-06-15 14:31:53 -0700716 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700717
Doug Zongker67369982010-07-07 13:53:32 -0700718 script.PatchCheck("%s:%sboot:%d:%s:%d:%s" %
719 (partition_type, partition_path,
720 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700721 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700722 so_far += source_boot.size
723 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700724
725 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700726 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800727
Doug Zongker05d3dea2009-06-22 11:32:31 -0700728 device_specific.IncrementalOTA_VerifyEnd()
729
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700730 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700731
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700732 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700733 script.Print("Erasing user data...")
734 script.FormatPartition("userdata")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700735
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700736 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700737 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
738 ["/"+i for i in sorted(source_data)
Doug Zongker3b949f02009-08-24 10:24:32 -0700739 if i not in target_data] +
740 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700741
Doug Zongker881dd402009-09-20 14:03:55 -0700742 script.ShowProgress(0.8, 0)
743 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
744 if updating_boot:
745 total_patch_size += target_boot.size
746 so_far = 0
747
748 script.Print("Patching system files...")
Doug Zongker5a482092010-02-17 16:09:18 -0800749 for fn, tf, sf, size, _ in patch_list:
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800750 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700751 so_far += tf.size
752 script.SetProgress(so_far / total_patch_size)
753
Doug Zongkereef39442009-04-02 12:14:19 -0700754 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700755 # Produce the boot image by applying a patch to the current
756 # contents of the boot partition, and write it back to the
757 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700758 script.Print("Patching boot image...")
Doug Zongker67369982010-07-07 13:53:32 -0700759 script.ApplyPatch("%s:%sboot:%d:%s:%d:%s"
760 % (partition_type, partition_path,
761 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700762 target_boot.size, target_boot.sha1),
763 "-",
764 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800765 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700766 so_far += target_boot.size
767 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700768 print "boot image changed; including."
769 else:
770 print "boot image unchanged; skipping."
771
772 if updating_recovery:
Doug Zongker73ef8252009-07-23 15:12:53 -0700773 # Is it better to generate recovery as a patch from the current
774 # boot image, or from the previous recovery image? For large
775 # updates with significant kernel changes, probably the former.
776 # For small updates where the kernel hasn't changed, almost
777 # certainly the latter. We pick the first option. Future
778 # complicated schemes may let us effectively use both.
779 #
780 # A wacky possibility: as long as there is room in the boot
781 # partition, include the binaries and image files from recovery in
782 # the boot image (though not in the ramdisk) so they can be used
783 # as fodder for constructing the recovery image.
Doug Zongker67369982010-07-07 13:53:32 -0700784 MakeRecoveryPatch(output_zip, target_recovery, target_boot, info)
Doug Zongker42265392010-02-12 10:21:00 -0800785 script.DeleteFiles(["/system/recovery-from-boot.p",
786 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700787 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700788 else:
789 print "recovery image unchanged; skipping."
790
Doug Zongker881dd402009-09-20 14:03:55 -0700791 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700792
Hristo Bojinov96be7202010-08-02 10:26:17 -0700793 (target_symlinks, target_retouch_dummies) = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700794
795 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700796 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700797 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700798 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700799
800 # Note that this call will mess up the tree of Items, so make sure
801 # we're done with it.
Hristo Bojinov96be7202010-08-02 10:26:17 -0700802 (source_symlinks, source_retouch_dummies) = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700803 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
804
805 # Delete all the symlinks in source that aren't in target. This
806 # needs to happen before verbatim files are unpacked, in case a
807 # symlink in the source is replaced by a real file in the target.
808 to_delete = []
809 for dest, link in source_symlinks:
810 if link not in target_symlinks_d:
811 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700812 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700813
814 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700815 script.Print("Unpacking new files...")
816 script.UnpackPackageDir("system", "/system")
817
Doug Zongker42265392010-02-12 10:21:00 -0800818 if updating_recovery:
819 script.Print("Unpacking new recovery...")
820 script.UnpackPackageDir("recovery", "/system")
821
Doug Zongker05d3dea2009-06-22 11:32:31 -0700822 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700823
824 # Create all the symlinks that don't already exist, or point to
825 # somewhere different than what we want. Delete each symlink before
826 # creating it, since the 'symlink' command won't overwrite.
827 to_create = []
828 for dest, link in target_symlinks:
829 if link in source_symlinks_d:
830 if dest != source_symlinks_d[link]:
831 to_create.append((dest, link))
832 else:
833 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700834 script.DeleteFiles([i[1] for i in to_create])
835 script.MakeSymlinks(to_create)
Hristo Bojinov96be7202010-08-02 10:26:17 -0700836 if OPTIONS.aslr_mode:
837 script.RetouchBinaries(target_retouch_files)
838 else:
839 script.UndoRetouchBinaries(target_retouch_files)
Doug Zongkereef39442009-04-02 12:14:19 -0700840
841 # Now that the symlinks are created, we can set all the
842 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700843 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700844
Doug Zongker881dd402009-09-20 14:03:55 -0700845 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700846 device_specific.IncrementalOTA_InstallEnd()
847
Doug Zongker1c390a22009-05-14 19:06:36 -0700848 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700849 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700850
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700851 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700852 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700853
854
855def main(argv):
856
857 def option_handler(o, a):
858 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700859 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700860 elif o in ("-k", "--package_key"):
861 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700862 elif o in ("-i", "--incremental_from"):
863 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700864 elif o in ("-w", "--wipe_user_data"):
865 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700866 elif o in ("-n", "--no_prereq"):
867 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700868 elif o in ("-e", "--extra_script"):
869 OPTIONS.extra_script = a
Hristo Bojinov96be7202010-08-02 10:26:17 -0700870 elif o in ("-a", "--use_aslr"):
871 OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -0700872 elif o in ("--worker_threads"):
873 OPTIONS.worker_threads = int(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700874 else:
875 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700876 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700877
878 args = common.ParseOptions(argv, __doc__,
Hristo Bojinov96be7202010-08-02 10:26:17 -0700879 extra_opts="b:k:i:d:wne:a",
Doug Zongkereef39442009-04-02 12:14:19 -0700880 extra_long_opts=["board_config=",
881 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700882 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700883 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700884 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700885 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700886 "worker_threads=",
887 "use_aslr"],
Doug Zongkereef39442009-04-02 12:14:19 -0700888 extra_option_handler=option_handler)
889
890 if len(args) != 2:
891 common.Usage(__doc__)
892 sys.exit(1)
893
Doug Zongker1c390a22009-05-14 19:06:36 -0700894 if OPTIONS.extra_script is not None:
895 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
896
Doug Zongkereef39442009-04-02 12:14:19 -0700897 print "unzipping target target-files..."
898 OPTIONS.input_tmp = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700899
Doug Zongkerc18736b2009-09-30 09:20:32 -0700900 if OPTIONS.device_specific is None:
901 # look for the device-specific tools extension location in the input
902 try:
903 f = open(os.path.join(OPTIONS.input_tmp, "META", "tool-extensions.txt"))
904 ds = f.read().strip()
905 f.close()
906 if ds:
907 ds = os.path.normpath(ds)
908 print "using device-specific extensions in", ds
909 OPTIONS.device_specific = ds
910 except IOError, e:
911 if e.errno == errno.ENOENT:
912 # nothing specified in the file
913 pass
914 else:
915 raise
916
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700917 info = common.LoadInfoDict()
918 common.LoadMaxSizes(info)
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700919 if not OPTIONS.max_image_size:
920 print
921 print " WARNING: Failed to load max image sizes; will not enforce"
922 print " image size limits."
923 print
924
Doug Zongkereef39442009-04-02 12:14:19 -0700925 OPTIONS.target_tmp = OPTIONS.input_tmp
926 input_zip = zipfile.ZipFile(args[0], "r")
927 if OPTIONS.package_key:
928 temp_zip_file = tempfile.NamedTemporaryFile()
929 output_zip = zipfile.ZipFile(temp_zip_file, "w",
930 compression=zipfile.ZIP_DEFLATED)
931 else:
932 output_zip = zipfile.ZipFile(args[1], "w",
933 compression=zipfile.ZIP_DEFLATED)
934
935 if OPTIONS.incremental_source is None:
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700936 WriteFullOTAPackage(input_zip, output_zip, info)
Doug Zongkereef39442009-04-02 12:14:19 -0700937 else:
938 print "unzipping source target-files..."
939 OPTIONS.source_tmp = common.UnzipTemp(OPTIONS.incremental_source)
940 source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
Doug Zongkerb4c7d322010-07-01 15:30:11 -0700941 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip, info)
Doug Zongkereef39442009-04-02 12:14:19 -0700942
943 output_zip.close()
944 if OPTIONS.package_key:
945 SignOutput(temp_zip_file.name, args[1])
946 temp_zip_file.close()
947
948 common.Cleanup()
949
950 print "done."
951
952
953if __name__ == '__main__':
954 try:
955 main(sys.argv[1:])
956 except common.ExternalError, e:
957 print
958 print " ERROR: %s" % (e,)
959 print
960 sys.exit(1)