blob: 2ef896f17eb1f9008a1b5bc53d91b911f6bf1555 [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
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Doug Zongkerdbfaae52009-04-21 17:12:54 -070040 -w (--wipe_user_data)
41 Generate an OTA package that will wipe the user data partition
42 when installed.
43
Doug Zongker962069c2009-04-23 11:41:58 -070044 -n (--no_prereq)
45 Omit the timestamp prereq check normally included at the top of
46 the build scripts (used for developer OTA packages which
47 legitimately need to go back and forth).
48
Doug Zongker1c390a22009-05-14 19:06:36 -070049 -e (--extra_script) <file>
50 Insert the contents of file at the end of the update script.
51
Hristo Bojinovdafb0422010-08-26 14:35:16 -070052 -a (--aslr_mode) <on|off>
53 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050054
Doug Zongkereef39442009-04-02 12:14:19 -070055"""
56
57import sys
58
59if sys.hexversion < 0x02040000:
60 print >> sys.stderr, "Python 2.4 or newer is required."
61 sys.exit(1)
62
63import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070064import errno
Doug Zongkereef39442009-04-02 12:14:19 -070065import os
66import re
Doug Zongkereef39442009-04-02 12:14:19 -070067import subprocess
68import tempfile
69import time
70import zipfile
71
davidcad0bb92011-03-15 14:21:38 +000072try:
73 from hashlib import sha1 as sha1
74except ImportError:
75 from sha import sha as sha1
76
Doug Zongkereef39442009-04-02 12:14:19 -070077import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070078import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070079
80OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -070081OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -070082OPTIONS.incremental_source = None
83OPTIONS.require_verbatim = set()
84OPTIONS.prohibit_verbatim = set(("system/build.prop",))
85OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070086OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070087OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070088OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070089OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070090OPTIONS.worker_threads = 3
Doug Zongkereef39442009-04-02 12:14:19 -070091
92def MostPopularKey(d, default):
93 """Given a dict, return the key corresponding to the largest
94 value. Returns 'default' if the dict is empty."""
95 x = [(v, k) for (k, v) in d.iteritems()]
96 if not x: return default
97 x.sort()
98 return x[-1][1]
99
100
101def IsSymlink(info):
102 """Return true if the zipfile.ZipInfo object passed in represents a
103 symlink."""
104 return (info.external_attr >> 16) == 0120777
105
Hristo Bojinov96be7202010-08-02 10:26:17 -0700106def IsRegular(info):
107 """Return true if the zipfile.ZipInfo object passed in represents a
108 symlink."""
109 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700110
Michael Runge37335b42013-11-06 17:59:29 -0800111def ClosestFileMatch(src, tgtfiles, existing):
112 """Returns the closest file match between a source file and list
113 of potential matches. The exact filename match is preferred,
114 then the sha1 is searched for, and finally a file with the same
115 basename is evaluated. Rename support in the updater-binary is
116 required for the latter checks to be used."""
117
118 result = tgtfiles.get("path:" + src.name)
119 if result is not None:
120 return result
121
122 if not OPTIONS.target_info_dict.get("update_rename_support", False):
123 return None
124
125 if src.size < 1000:
126 return None
127
128 result = tgtfiles.get("sha1:" + src.sha1)
129 if result is not None and existing.get(result.name) is None:
130 return result
131 result = tgtfiles.get("file:" + src.name.split("/")[-1])
132 if result is not None and existing.get(result.name) is None:
133 return result
134 return None
135
Doug Zongkereef39442009-04-02 12:14:19 -0700136class Item:
137 """Items represent the metadata (user, group, mode) of files and
138 directories in the system image."""
139 ITEMS = {}
140 def __init__(self, name, dir=False):
141 self.name = name
142 self.uid = None
143 self.gid = None
144 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700145 self.selabel = None
146 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700147 self.dir = dir
148
149 if name:
150 self.parent = Item.Get(os.path.dirname(name), dir=True)
151 self.parent.children.append(self)
152 else:
153 self.parent = None
154 if dir:
155 self.children = []
156
157 def Dump(self, indent=0):
158 if self.uid is not None:
159 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
160 else:
161 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
162 if self.dir:
163 print "%s%s" % (" "*indent, self.descendants)
164 print "%s%s" % (" "*indent, self.best_subtree)
165 for i in self.children:
166 i.Dump(indent=indent+1)
167
168 @classmethod
169 def Get(cls, name, dir=False):
170 if name not in cls.ITEMS:
171 cls.ITEMS[name] = Item(name, dir=dir)
172 return cls.ITEMS[name]
173
174 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700175 def GetMetadata(cls, input_zip):
176
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700177 # The target_files contains a record of what the uid,
178 # gid, and mode are supposed to be.
179 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700180
181 for line in output.split("\n"):
182 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700183 columns = line.split()
184 name, uid, gid, mode = columns[:4]
185 selabel = None
186 capabilities = None
187
188 # After the first 4 columns, there are a series of key=value
189 # pairs. Extract out the fields we care about.
190 for element in columns[4:]:
191 key, value = element.split("=")
192 if key == "selabel":
193 selabel = value
194 if key == "capabilities":
195 capabilities = value
196
Doug Zongker283e2a12010-03-15 17:52:32 -0700197 i = cls.ITEMS.get(name, None)
198 if i is not None:
199 i.uid = int(uid)
200 i.gid = int(gid)
201 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700202 i.selabel = selabel
203 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700204 if i.dir:
205 i.children.sort(key=lambda i: i.name)
206
207 # set metadata for the files generated by this script.
208 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700209 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700210 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700211 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700212
213 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700214 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
215 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700216 set_perm to correctly chown/chmod all the files to their desired
217 values. Recursively calls itself for all descendants.
218
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700219 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700220 all descendants of this node. (dmode or fmode may be None.) Also
221 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700222 dmode, fmode, selabel, capabilities) tuple that will match the most
223 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700224 """
225
226 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700227 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700228 for i in self.children:
229 if i.dir:
230 for k, v in i.CountChildMetadata().iteritems():
231 d[k] = d.get(k, 0) + v
232 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700233 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700234 d[k] = d.get(k, 0) + 1
235
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700236 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
237 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700238
239 # First, find the (uid, gid) pair that matches the most
240 # descendants.
241 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700242 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700243 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
244 ug = MostPopularKey(ug, (0, 0))
245
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700246 # Now find the dmode, fmode, selabel, and capabilities that match
247 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700248 best_dmode = (0, 0755)
249 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700250 best_selabel = (0, None)
251 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700252 for k, count in d.iteritems():
253 if k[:2] != ug: continue
254 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
255 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700256 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
257 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
258 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700259
260 return d
261
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700262 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700263 """Append set_perm/set_perm_recursive commands to 'script' to
264 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700265 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700266
267 self.CountChildMetadata()
268
269 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700270 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700271 # item (and all its children) have already been set to. We only
272 # need to issue set_perm/set_perm_recursive commands if we're
273 # supposed to be something different.
274 if item.dir:
275 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700276 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700277 current = item.best_subtree
278
279 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700280 item.mode != current[2] or item.selabel != current[4] or \
281 item.capabilities != current[5]:
282 script.SetPermissions("/"+item.name, item.uid, item.gid,
283 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700284
285 for i in item.children:
286 recurse(i, current)
287 else:
288 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700289 item.mode != current[3] or item.selabel != current[4] or \
290 item.capabilities != current[5]:
291 script.SetPermissions("/"+item.name, item.uid, item.gid,
292 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700293
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700294 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700295
296
297def CopySystemFiles(input_zip, output_zip=None,
298 substitute=None):
299 """Copies files underneath system/ in the input zip to the output
300 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800301 list of symlinks. output_zip may be None, in which case the copy is
302 skipped (but the other side effects still happen). substitute is an
303 optional dict of {output filename: contents} to be output instead of
304 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700305 """
306
307 symlinks = []
308
309 for info in input_zip.infolist():
310 if info.filename.startswith("SYSTEM/"):
311 basefilename = info.filename[7:]
312 if IsSymlink(info):
313 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700314 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700315 else:
316 info2 = copy.copy(info)
317 fn = info2.filename = "system/" + basefilename
318 if substitute and fn in substitute and substitute[fn] is None:
319 continue
320 if output_zip is not None:
321 if substitute and fn in substitute:
322 data = substitute[fn]
323 else:
324 data = input_zip.read(info.filename)
325 output_zip.writestr(info2, data)
326 if fn.endswith("/"):
327 Item.Get(fn[:-1], dir=True)
328 else:
329 Item.Get(fn, dir=False)
330
331 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800332 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700333
334
Doug Zongkereef39442009-04-02 12:14:19 -0700335def SignOutput(temp_zip_name, output_zip_name):
336 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
337 pw = key_passwords[OPTIONS.package_key]
338
Doug Zongker951495f2009-08-14 12:44:19 -0700339 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
340 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700341
342
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700343def AppendAssertions(script, info_dict):
344 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700345 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700346
Doug Zongkereef39442009-04-02 12:14:19 -0700347
Doug Zongkerb32161a2012-08-21 10:33:44 -0700348def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700349 """Generate a binary patch that creates the recovery image starting
350 with the boot image. (Most of the space in these images is just the
351 kernel, which is identical for the two, so the resulting patch
352 should be efficient.) Add it to the output zip, along with a shell
353 script that is run from init.rc on first boot to actually do the
354 patching and install the new recovery image.
355
356 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700357 corresponding images. info should be the dictionary returned by
358 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700359
360 Returns an Item for the shell script, which must be made
361 executable.
362 """
363
Doug Zongkerb32161a2012-08-21 10:33:44 -0700364 diff_program = ["imgdiff"]
365 path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
366 if os.path.exists(path):
367 diff_program.append("-b")
368 diff_program.append(path)
369 bonus_args = "-b /system/etc/recovery-resource.dat"
370 else:
371 bonus_args = ""
372
373 d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
Doug Zongker761e6422009-09-25 10:45:39 -0700374 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700375 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700376 Item.Get("system/recovery-from-boot.p", dir=False)
377
Doug Zongker96a57e72010-09-26 14:57:41 -0700378 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
379 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700380
Doug Zongker73ef8252009-07-23 15:12:53 -0700381 sh = """#!/system/bin/sh
Doug Zongker0276d182011-12-02 10:46:59 -0800382if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700383 log -t recovery "Installing new recovery image"
Doug Zongkerb32161a2012-08-21 10:33:44 -0700384 applypatch %(bonus_args)s %(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 -0700385else
386 log -t recovery "Recovery image already installed"
387fi
388""" % { 'boot_size': boot_img.size,
389 'boot_sha1': boot_img.sha1,
Doug Zongker73ef8252009-07-23 15:12:53 -0700390 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700391 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700392 'boot_type': boot_type,
393 'boot_device': boot_device,
394 'recovery_type': recovery_type,
395 'recovery_device': recovery_device,
Doug Zongkerb32161a2012-08-21 10:33:44 -0700396 'bonus_args': bonus_args,
Doug Zongker67369982010-07-07 13:53:32 -0700397 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700398 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700399 return Item.Get("system/etc/install-recovery.sh", dir=False)
400
401
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700402def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700403 # TODO: how to determine this? We don't know what version it will
404 # be installed on top of. For now, we expect the API just won't
405 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700406 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700407
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700408 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
409 OPTIONS.info_dict),
410 "pre-device": GetBuildProp("ro.product.device",
411 OPTIONS.info_dict),
412 "post-timestamp": GetBuildProp("ro.build.date.utc",
413 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700414 }
415
Doug Zongker05d3dea2009-06-22 11:32:31 -0700416 device_specific = common.DeviceSpecificParams(
417 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700418 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700419 output_zip=output_zip,
420 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700421 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700422 metadata=metadata,
423 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700424
Doug Zongker962069c2009-04-23 11:41:58 -0700425 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700426 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700427 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
428 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700429
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700430 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700431 device_specific.FullOTA_Assertions()
Doug Zongkere5ff5902012-01-17 10:55:37 -0800432 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700433
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700434 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700435
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700436 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700437 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700438
Kenny Rootf32dc712012-04-08 10:42:34 -0700439 if "selinux_fc" in OPTIONS.info_dict:
440 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500441
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700442 script.FormatPartition("/system")
443 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700444 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700445 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700446
Doug Zongker1807e702012-02-28 12:21:08 -0800447 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700448 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700449
Doug Zongker55d93282011-01-25 17:03:34 -0800450 boot_img = common.GetBootableImage("boot.img", "boot.img",
451 OPTIONS.input_tmp, "BOOT")
452 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
453 OPTIONS.input_tmp, "RECOVERY")
Doug Zongkerb32161a2012-08-21 10:33:44 -0700454 MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700455
Doug Zongker283e2a12010-03-15 17:52:32 -0700456 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700457 Item.Get("system").SetPermissions(script)
458
Doug Zongker37974732010-09-16 17:44:38 -0700459 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700460 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700461 script.ShowProgress(0.2, 0)
462
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700463 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700464 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700465
466 script.ShowProgress(0.1, 0)
467 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700468
Doug Zongker1c390a22009-05-14 19:06:36 -0700469 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700470 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700471
Doug Zongker14833602010-02-02 13:12:04 -0800472 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700473 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700474 WriteMetadata(metadata, output_zip)
475
Stephen Smalley56882bf2012-02-09 13:36:21 -0500476def WritePolicyConfig(file_context, output_zip):
477 f = open(file_context, 'r');
478 basename = os.path.basename(file_context)
479 common.ZipWriteStr(output_zip, basename, f.read())
480
Doug Zongker2ea21062010-04-28 16:05:21 -0700481
482def WriteMetadata(metadata, output_zip):
483 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
484 "".join(["%s=%s\n" % kv
485 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700486
Doug Zongkereef39442009-04-02 12:14:19 -0700487def LoadSystemFiles(z):
488 """Load all the files from SYSTEM/... in a given target-files
489 ZipFile, and return a dict of {filename: File object}."""
490 out = {}
491 for info in z.infolist():
492 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700493 basefilename = info.filename[7:]
494 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700495 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700496 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800497 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700498
499
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700500def GetBuildProp(prop, info_dict):
501 """Return the fingerprint of the build of a given target-files info_dict."""
502 try:
503 return info_dict.get("build.prop", {})[prop]
504 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700505 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700506
507
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700508def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700509 source_version = OPTIONS.source_info_dict["recovery_api_version"]
510 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700511
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700512 if source_version == 0:
513 print ("WARNING: generating edify script for a source that "
514 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700515 script = edify_generator.EdifyGenerator(source_version,
516 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700517
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700518 metadata = {"pre-device": GetBuildProp("ro.product.device",
519 OPTIONS.source_info_dict),
520 "post-timestamp": GetBuildProp("ro.build.date.utc",
521 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700522 }
523
Doug Zongker05d3dea2009-06-22 11:32:31 -0700524 device_specific = common.DeviceSpecificParams(
525 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800526 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700527 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800528 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700529 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700530 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700531 metadata=metadata,
532 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700533
Doug Zongkereef39442009-04-02 12:14:19 -0700534 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800535 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700536 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800537 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700538
539 verbatim_targets = []
540 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700541 diffs = []
Michael Runge37335b42013-11-06 17:59:29 -0800542 renames = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700543 largest_source_size = 0
Michael Runge37335b42013-11-06 17:59:29 -0800544
545 matching_file_cache = {}
546 for fn in source_data.keys():
547 sf = source_data[fn]
548 assert fn == sf.name
549 matching_file_cache["path:" + fn] = sf
550 # Only allow eligability for filename/sha matching
551 # if there isn't a perfect path match.
552 if target_data.get(sf.name) is None:
553 matching_file_cache["file:" + fn.split("/")[-1]] = sf
554 matching_file_cache["sha:" + sf.sha1] = sf
555
Doug Zongkereef39442009-04-02 12:14:19 -0700556 for fn in sorted(target_data.keys()):
557 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700558 assert fn == tf.name
Michael Runge37335b42013-11-06 17:59:29 -0800559 sf = ClosestFileMatch(tf, matching_file_cache, renames)
560 if sf is not None and sf.name != tf.name:
561 print "File has moved from " + sf.name + " to " + tf.name
562 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700563
564 if sf is None or fn in OPTIONS.require_verbatim:
565 # This file should be included verbatim
566 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700567 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700568 print "send", fn, "verbatim"
569 tf.AddToZip(output_zip)
570 verbatim_targets.append((fn, tf.size))
571 elif tf.sha1 != sf.sha1:
572 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700573 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700574 else:
Michael Runge37335b42013-11-06 17:59:29 -0800575 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700576 pass
577
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700578 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700579
580 for diff in diffs:
581 tf, sf, d = diff.GetPatch()
582 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
583 # patch is almost as big as the file; don't bother patching
584 tf.AddToZip(output_zip)
585 verbatim_targets.append((tf.name, tf.size))
586 else:
Michael Runge37335b42013-11-06 17:59:29 -0800587 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
588 patch_list.append((sf.name, tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700589 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700590
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700591 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
592 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700593 metadata["pre-build"] = source_fp
594 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700595
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700596 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700597 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700598
Doug Zongker55d93282011-01-25 17:03:34 -0800599 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700600 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
601 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800602 target_boot = common.GetBootableImage(
603 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker5da317e2009-06-02 13:38:17 -0700604 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700605
Doug Zongker55d93282011-01-25 17:03:34 -0800606 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700607 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
608 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800609 target_recovery = common.GetBootableImage(
610 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700611 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700612
Doug Zongker881dd402009-09-20 14:03:55 -0700613 # Here's how we divide up the progress bar:
614 # 0.1 for verifying the start state (PatchCheck calls)
615 # 0.8 for applying patches (ApplyPatch calls)
616 # 0.1 for unpacking verbatim files, symlinking, and doing the
617 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700618
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700619 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700620 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700621
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700622 script.Print("Verifying current system...")
623
Doug Zongkere5ff5902012-01-17 10:55:37 -0800624 device_specific.IncrementalOTA_VerifyBegin()
625
Doug Zongker881dd402009-09-20 14:03:55 -0700626 script.ShowProgress(0.1, 0)
627 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
628 if updating_boot:
629 total_verify_size += source_boot.size
630 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700631
Doug Zongker5a482092010-02-17 16:09:18 -0800632 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700633 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700634 so_far += sf.size
635 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700636
Doug Zongker5da317e2009-06-02 13:38:17 -0700637 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700638 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700639 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700640 print "boot target: %d source: %d diff: %d" % (
641 target_boot.size, source_boot.size, len(d))
642
Doug Zongker048e7ca2009-06-15 14:31:53 -0700643 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700644
Doug Zongker96a57e72010-09-26 14:57:41 -0700645 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700646
647 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
648 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700649 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700650 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700651 so_far += source_boot.size
652 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700653
654 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700655 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800656
Doug Zongker05d3dea2009-06-22 11:32:31 -0700657 device_specific.IncrementalOTA_VerifyEnd()
658
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700659 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700660
Doug Zongkere5ff5902012-01-17 10:55:37 -0800661 device_specific.IncrementalOTA_InstallBegin()
662
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700663 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700664 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700665 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700666
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700667 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700668 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
669 ["/"+i for i in sorted(source_data)
Michael Runge37335b42013-11-06 17:59:29 -0800670 if i not in target_data and
671 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700672 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700673
Doug Zongker881dd402009-09-20 14:03:55 -0700674 script.ShowProgress(0.8, 0)
675 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
676 if updating_boot:
677 total_patch_size += target_boot.size
678 so_far = 0
679
680 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700681 deferred_patch_list = []
682 for item in patch_list:
683 fn, tf, sf, size, _ = item
684 if tf.name == "system/build.prop":
685 deferred_patch_list.append(item)
686 continue
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800687 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700688 so_far += tf.size
689 script.SetProgress(so_far / total_patch_size)
690
Doug Zongkereef39442009-04-02 12:14:19 -0700691 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700692 # Produce the boot image by applying a patch to the current
693 # contents of the boot partition, and write it back to the
694 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700695 script.Print("Patching boot image...")
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700696 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
697 % (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700698 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700699 target_boot.size, target_boot.sha1),
700 "-",
701 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800702 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700703 so_far += target_boot.size
704 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700705 print "boot image changed; including."
706 else:
707 print "boot image unchanged; skipping."
708
709 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700710 # Recovery is generated as a patch using both the boot image
711 # (which contains the same linux kernel as recovery) and the file
712 # /system/etc/recovery-resource.dat (which contains all the images
713 # used in the recovery UI) as sources. This lets us minimize the
714 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700715 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700716 # For older builds where recovery-resource.dat is not present, we
717 # use only the boot image as the source.
718
719 MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
720 target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800721 script.DeleteFiles(["/system/recovery-from-boot.p",
722 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700723 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700724 else:
725 print "recovery image unchanged; skipping."
726
Doug Zongker881dd402009-09-20 14:03:55 -0700727 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700728
Doug Zongker1807e702012-02-28 12:21:08 -0800729 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700730
731 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700732 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700733 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700734 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700735
736 # Note that this call will mess up the tree of Items, so make sure
737 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800738 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700739 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
740
741 # Delete all the symlinks in source that aren't in target. This
742 # needs to happen before verbatim files are unpacked, in case a
743 # symlink in the source is replaced by a real file in the target.
744 to_delete = []
745 for dest, link in source_symlinks:
746 if link not in target_symlinks_d:
747 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700748 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700749
750 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700751 script.Print("Unpacking new files...")
752 script.UnpackPackageDir("system", "/system")
753
Doug Zongker42265392010-02-12 10:21:00 -0800754 if updating_recovery:
755 script.Print("Unpacking new recovery...")
756 script.UnpackPackageDir("recovery", "/system")
757
Michael Runge37335b42013-11-06 17:59:29 -0800758 if len(renames) > 0:
759 script.Print("Renaming files...")
760
761 for src in renames:
762 print "Renaming " + src + " to " + renames[src].name
763 script.RenameFile(src, renames[src].name)
764
Doug Zongker05d3dea2009-06-22 11:32:31 -0700765 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700766
767 # Create all the symlinks that don't already exist, or point to
768 # somewhere different than what we want. Delete each symlink before
769 # creating it, since the 'symlink' command won't overwrite.
770 to_create = []
771 for dest, link in target_symlinks:
772 if link in source_symlinks_d:
773 if dest != source_symlinks_d[link]:
774 to_create.append((dest, link))
775 else:
776 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700777 script.DeleteFiles([i[1] for i in to_create])
778 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700779
780 # Now that the symlinks are created, we can set all the
781 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700782 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700783
Doug Zongker881dd402009-09-20 14:03:55 -0700784 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700785 device_specific.IncrementalOTA_InstallEnd()
786
Doug Zongker1c390a22009-05-14 19:06:36 -0700787 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700788 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700789
Doug Zongkere92f15a2011-08-26 13:46:40 -0700790 # Patch the build.prop file last, so if something fails but the
791 # device can still come up, it appears to be the old build and will
792 # get set the OTA package again to retry.
793 script.Print("Patching remaining system files...")
794 for item in deferred_patch_list:
795 fn, tf, sf, size, _ = item
796 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700797 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700798
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700799 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700800 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700801
802
803def main(argv):
804
805 def option_handler(o, a):
806 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700807 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700808 elif o in ("-k", "--package_key"):
809 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700810 elif o in ("-i", "--incremental_from"):
811 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700812 elif o in ("-w", "--wipe_user_data"):
813 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700814 elif o in ("-n", "--no_prereq"):
815 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700816 elif o in ("-e", "--extra_script"):
817 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700818 elif o in ("-a", "--aslr_mode"):
819 if a in ("on", "On", "true", "True", "yes", "Yes"):
820 OPTIONS.aslr_mode = True
821 else:
822 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700823 elif o in ("--worker_threads"):
824 OPTIONS.worker_threads = int(a)
Doug Zongkereef39442009-04-02 12:14:19 -0700825 else:
826 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700827 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700828
829 args = common.ParseOptions(argv, __doc__,
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700830 extra_opts="b:k:i:d:wne:a:",
Doug Zongkereef39442009-04-02 12:14:19 -0700831 extra_long_opts=["board_config=",
832 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700833 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700834 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700835 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700836 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700837 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700838 "aslr_mode=",
839 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700840 extra_option_handler=option_handler)
841
842 if len(args) != 2:
843 common.Usage(__doc__)
844 sys.exit(1)
845
Doug Zongker1c390a22009-05-14 19:06:36 -0700846 if OPTIONS.extra_script is not None:
847 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
848
Doug Zongkereef39442009-04-02 12:14:19 -0700849 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800850 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700851
Doug Zongkereef39442009-04-02 12:14:19 -0700852 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -0700853 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -0700854
855 # If this image was originally labelled with SELinux contexts, make sure we
856 # also apply the labels in our new image. During building, the "file_contexts"
857 # is in the out/ directory tree, but for repacking from target-files.zip it's
858 # in the root directory of the ramdisk.
859 if "selinux_fc" in OPTIONS.info_dict:
860 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
861 "file_contexts")
862
Doug Zongker37974732010-09-16 17:44:38 -0700863 if OPTIONS.verbose:
864 print "--- target info ---"
865 common.DumpInfoDict(OPTIONS.info_dict)
866
867 if OPTIONS.device_specific is None:
868 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
869 if OPTIONS.device_specific is not None:
870 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
871 print "using device-specific extensions in", OPTIONS.device_specific
872
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700873 temp_zip_file = tempfile.NamedTemporaryFile()
874 output_zip = zipfile.ZipFile(temp_zip_file, "w",
875 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -0700876
877 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700878 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700879 if OPTIONS.package_key is None:
880 OPTIONS.package_key = OPTIONS.info_dict.get(
881 "default_system_dev_certificate",
882 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -0700883 else:
884 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800885 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -0700886 OPTIONS.target_info_dict = OPTIONS.info_dict
887 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700888 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -0700889 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700890 "default_system_dev_certificate",
891 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -0700892 if OPTIONS.verbose:
893 print "--- source info ---"
894 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700895 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700896
897 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700898
899 SignOutput(temp_zip_file.name, args[1])
900 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700901
902 common.Cleanup()
903
904 print "done."
905
906
907if __name__ == '__main__':
908 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -0800909 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -0700910 main(sys.argv[1:])
911 except common.ExternalError, e:
912 print
913 print " ERROR: %s" % (e,)
914 print
915 sys.exit(1)