blob: b7e66135ca87a5928ace1f83c17844f1f035985b [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
Takeshi Kanemotoe153b342013-11-14 17:20:50 +090091OPTIONS.no_signing = False
Doug Zongkereef39442009-04-02 12:14:19 -070092
93def MostPopularKey(d, default):
94 """Given a dict, return the key corresponding to the largest
95 value. Returns 'default' if the dict is empty."""
96 x = [(v, k) for (k, v) in d.iteritems()]
97 if not x: return default
98 x.sort()
99 return x[-1][1]
100
101
102def IsSymlink(info):
103 """Return true if the zipfile.ZipInfo object passed in represents a
104 symlink."""
105 return (info.external_attr >> 16) == 0120777
106
Hristo Bojinov96be7202010-08-02 10:26:17 -0700107def IsRegular(info):
108 """Return true if the zipfile.ZipInfo object passed in represents a
109 symlink."""
110 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700111
Michael Runge37335b42013-11-06 17:59:29 -0800112def ClosestFileMatch(src, tgtfiles, existing):
113 """Returns the closest file match between a source file and list
114 of potential matches. The exact filename match is preferred,
115 then the sha1 is searched for, and finally a file with the same
116 basename is evaluated. Rename support in the updater-binary is
117 required for the latter checks to be used."""
118
119 result = tgtfiles.get("path:" + src.name)
120 if result is not None:
121 return result
122
123 if not OPTIONS.target_info_dict.get("update_rename_support", False):
124 return None
125
126 if src.size < 1000:
127 return None
128
129 result = tgtfiles.get("sha1:" + src.sha1)
130 if result is not None and existing.get(result.name) is None:
131 return result
132 result = tgtfiles.get("file:" + src.name.split("/")[-1])
133 if result is not None and existing.get(result.name) is None:
134 return result
135 return None
136
Doug Zongkereef39442009-04-02 12:14:19 -0700137class Item:
138 """Items represent the metadata (user, group, mode) of files and
139 directories in the system image."""
140 ITEMS = {}
141 def __init__(self, name, dir=False):
142 self.name = name
143 self.uid = None
144 self.gid = None
145 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700146 self.selabel = None
147 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700148 self.dir = dir
149
150 if name:
151 self.parent = Item.Get(os.path.dirname(name), dir=True)
152 self.parent.children.append(self)
153 else:
154 self.parent = None
155 if dir:
156 self.children = []
157
158 def Dump(self, indent=0):
159 if self.uid is not None:
160 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
161 else:
162 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
163 if self.dir:
164 print "%s%s" % (" "*indent, self.descendants)
165 print "%s%s" % (" "*indent, self.best_subtree)
166 for i in self.children:
167 i.Dump(indent=indent+1)
168
169 @classmethod
170 def Get(cls, name, dir=False):
171 if name not in cls.ITEMS:
172 cls.ITEMS[name] = Item(name, dir=dir)
173 return cls.ITEMS[name]
174
175 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700176 def GetMetadata(cls, input_zip):
177
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700178 # The target_files contains a record of what the uid,
179 # gid, and mode are supposed to be.
180 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700181
182 for line in output.split("\n"):
183 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700184 columns = line.split()
185 name, uid, gid, mode = columns[:4]
186 selabel = None
187 capabilities = None
188
189 # After the first 4 columns, there are a series of key=value
190 # pairs. Extract out the fields we care about.
191 for element in columns[4:]:
192 key, value = element.split("=")
193 if key == "selabel":
194 selabel = value
195 if key == "capabilities":
196 capabilities = value
197
Doug Zongker283e2a12010-03-15 17:52:32 -0700198 i = cls.ITEMS.get(name, None)
199 if i is not None:
200 i.uid = int(uid)
201 i.gid = int(gid)
202 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700203 i.selabel = selabel
204 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700205 if i.dir:
206 i.children.sort(key=lambda i: i.name)
207
208 # set metadata for the files generated by this script.
209 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700210 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700211 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700212 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700213
214 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700215 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
216 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700217 set_perm to correctly chown/chmod all the files to their desired
218 values. Recursively calls itself for all descendants.
219
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700220 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700221 all descendants of this node. (dmode or fmode may be None.) Also
222 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700223 dmode, fmode, selabel, capabilities) tuple that will match the most
224 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700225 """
226
227 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700228 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700229 for i in self.children:
230 if i.dir:
231 for k, v in i.CountChildMetadata().iteritems():
232 d[k] = d.get(k, 0) + v
233 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700234 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700235 d[k] = d.get(k, 0) + 1
236
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700237 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
238 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700239
240 # First, find the (uid, gid) pair that matches the most
241 # descendants.
242 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700243 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700244 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
245 ug = MostPopularKey(ug, (0, 0))
246
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700247 # Now find the dmode, fmode, selabel, and capabilities that match
248 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700249 best_dmode = (0, 0755)
250 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700251 best_selabel = (0, None)
252 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700253 for k, count in d.iteritems():
254 if k[:2] != ug: continue
255 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
256 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
258 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
259 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700260
261 return d
262
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700263 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700264 """Append set_perm/set_perm_recursive commands to 'script' to
265 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700266 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700267
268 self.CountChildMetadata()
269
270 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700271 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700272 # item (and all its children) have already been set to. We only
273 # need to issue set_perm/set_perm_recursive commands if we're
274 # supposed to be something different.
275 if item.dir:
276 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700277 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700278 current = item.best_subtree
279
280 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700281 item.mode != current[2] or item.selabel != current[4] or \
282 item.capabilities != current[5]:
283 script.SetPermissions("/"+item.name, item.uid, item.gid,
284 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700285
286 for i in item.children:
287 recurse(i, current)
288 else:
289 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700290 item.mode != current[3] or item.selabel != current[4] or \
291 item.capabilities != current[5]:
292 script.SetPermissions("/"+item.name, item.uid, item.gid,
293 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700294
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700295 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700296
297
298def CopySystemFiles(input_zip, output_zip=None,
299 substitute=None):
300 """Copies files underneath system/ in the input zip to the output
301 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800302 list of symlinks. output_zip may be None, in which case the copy is
303 skipped (but the other side effects still happen). substitute is an
304 optional dict of {output filename: contents} to be output instead of
305 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700306 """
307
308 symlinks = []
309
310 for info in input_zip.infolist():
311 if info.filename.startswith("SYSTEM/"):
312 basefilename = info.filename[7:]
313 if IsSymlink(info):
314 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700315 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700316 else:
317 info2 = copy.copy(info)
318 fn = info2.filename = "system/" + basefilename
319 if substitute and fn in substitute and substitute[fn] is None:
320 continue
321 if output_zip is not None:
322 if substitute and fn in substitute:
323 data = substitute[fn]
324 else:
325 data = input_zip.read(info.filename)
326 output_zip.writestr(info2, data)
327 if fn.endswith("/"):
328 Item.Get(fn[:-1], dir=True)
329 else:
330 Item.Get(fn, dir=False)
331
332 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800333 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700334
335
Doug Zongkereef39442009-04-02 12:14:19 -0700336def SignOutput(temp_zip_name, output_zip_name):
337 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
338 pw = key_passwords[OPTIONS.package_key]
339
Doug Zongker951495f2009-08-14 12:44:19 -0700340 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
341 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700342
343
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700344def AppendAssertions(script, info_dict):
345 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700346 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700347
Doug Zongkereef39442009-04-02 12:14:19 -0700348
Doug Zongkerb32161a2012-08-21 10:33:44 -0700349def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700350 """Generate a binary patch that creates the recovery image starting
351 with the boot image. (Most of the space in these images is just the
352 kernel, which is identical for the two, so the resulting patch
353 should be efficient.) Add it to the output zip, along with a shell
354 script that is run from init.rc on first boot to actually do the
355 patching and install the new recovery image.
356
357 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700358 corresponding images. info should be the dictionary returned by
359 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700360
361 Returns an Item for the shell script, which must be made
362 executable.
363 """
364
Doug Zongkerb32161a2012-08-21 10:33:44 -0700365 diff_program = ["imgdiff"]
366 path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
367 if os.path.exists(path):
368 diff_program.append("-b")
369 diff_program.append(path)
370 bonus_args = "-b /system/etc/recovery-resource.dat"
371 else:
372 bonus_args = ""
373
374 d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
Doug Zongker761e6422009-09-25 10:45:39 -0700375 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700376 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700377 Item.Get("system/recovery-from-boot.p", dir=False)
378
Doug Zongker96a57e72010-09-26 14:57:41 -0700379 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
380 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700381
Doug Zongker73ef8252009-07-23 15:12:53 -0700382 sh = """#!/system/bin/sh
Doug Zongker0276d182011-12-02 10:46:59 -0800383if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700384 log -t recovery "Installing new recovery image"
Doug Zongkerb32161a2012-08-21 10:33:44 -0700385 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 -0700386else
387 log -t recovery "Recovery image already installed"
388fi
389""" % { 'boot_size': boot_img.size,
390 'boot_sha1': boot_img.sha1,
Doug Zongker73ef8252009-07-23 15:12:53 -0700391 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700392 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700393 'boot_type': boot_type,
394 'boot_device': boot_device,
395 'recovery_type': recovery_type,
396 'recovery_device': recovery_device,
Doug Zongkerb32161a2012-08-21 10:33:44 -0700397 'bonus_args': bonus_args,
Doug Zongker67369982010-07-07 13:53:32 -0700398 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700399 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700400 return Item.Get("system/etc/install-recovery.sh", dir=False)
401
402
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700403def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700404 # TODO: how to determine this? We don't know what version it will
405 # be installed on top of. For now, we expect the API just won't
406 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700407 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700408
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700409 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
410 OPTIONS.info_dict),
411 "pre-device": GetBuildProp("ro.product.device",
412 OPTIONS.info_dict),
413 "post-timestamp": GetBuildProp("ro.build.date.utc",
414 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700415 }
416
Doug Zongker05d3dea2009-06-22 11:32:31 -0700417 device_specific = common.DeviceSpecificParams(
418 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700419 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700420 output_zip=output_zip,
421 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700422 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700423 metadata=metadata,
424 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700425
Doug Zongker962069c2009-04-23 11:41:58 -0700426 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700427 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700428 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
429 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700430
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700431 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700432 device_specific.FullOTA_Assertions()
Doug Zongkere5ff5902012-01-17 10:55:37 -0800433 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700434
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700435 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700436
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700437 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700438 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700439
Kenny Rootf32dc712012-04-08 10:42:34 -0700440 if "selinux_fc" in OPTIONS.info_dict:
441 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500442
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700443 script.FormatPartition("/system")
444 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700445 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700446 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700447
Doug Zongker1807e702012-02-28 12:21:08 -0800448 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700449 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700450
Doug Zongker55d93282011-01-25 17:03:34 -0800451 boot_img = common.GetBootableImage("boot.img", "boot.img",
452 OPTIONS.input_tmp, "BOOT")
453 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
454 OPTIONS.input_tmp, "RECOVERY")
Doug Zongkerb32161a2012-08-21 10:33:44 -0700455 MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700456
Doug Zongker283e2a12010-03-15 17:52:32 -0700457 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700458 Item.Get("system").SetPermissions(script)
459
Doug Zongker37974732010-09-16 17:44:38 -0700460 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700461 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700462 script.ShowProgress(0.2, 0)
463
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700464 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700465 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700466
467 script.ShowProgress(0.1, 0)
468 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700469
Doug Zongker1c390a22009-05-14 19:06:36 -0700470 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700471 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700472
Doug Zongker14833602010-02-02 13:12:04 -0800473 script.UnmountAll()
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700474 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700475 WriteMetadata(metadata, output_zip)
476
Stephen Smalley56882bf2012-02-09 13:36:21 -0500477def WritePolicyConfig(file_context, output_zip):
478 f = open(file_context, 'r');
479 basename = os.path.basename(file_context)
480 common.ZipWriteStr(output_zip, basename, f.read())
481
Doug Zongker2ea21062010-04-28 16:05:21 -0700482
483def WriteMetadata(metadata, output_zip):
484 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
485 "".join(["%s=%s\n" % kv
486 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700487
Doug Zongkereef39442009-04-02 12:14:19 -0700488def LoadSystemFiles(z):
489 """Load all the files from SYSTEM/... in a given target-files
490 ZipFile, and return a dict of {filename: File object}."""
491 out = {}
492 for info in z.infolist():
493 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700494 basefilename = info.filename[7:]
495 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700496 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700497 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800498 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700499
500
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700501def GetBuildProp(prop, info_dict):
502 """Return the fingerprint of the build of a given target-files info_dict."""
503 try:
504 return info_dict.get("build.prop", {})[prop]
505 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700506 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700507
508
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700509def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700510 source_version = OPTIONS.source_info_dict["recovery_api_version"]
511 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700512
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700513 if source_version == 0:
514 print ("WARNING: generating edify script for a source that "
515 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700516 script = edify_generator.EdifyGenerator(source_version,
517 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700518
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700519 metadata = {"pre-device": GetBuildProp("ro.product.device",
520 OPTIONS.source_info_dict),
521 "post-timestamp": GetBuildProp("ro.build.date.utc",
522 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700523 }
524
Doug Zongker05d3dea2009-06-22 11:32:31 -0700525 device_specific = common.DeviceSpecificParams(
526 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800527 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700528 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800529 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700530 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700531 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700532 metadata=metadata,
533 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700534
Doug Zongkereef39442009-04-02 12:14:19 -0700535 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800536 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700537 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800538 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700539
540 verbatim_targets = []
541 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700542 diffs = []
Michael Runge37335b42013-11-06 17:59:29 -0800543 renames = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700544 largest_source_size = 0
Michael Runge37335b42013-11-06 17:59:29 -0800545
546 matching_file_cache = {}
547 for fn in source_data.keys():
548 sf = source_data[fn]
549 assert fn == sf.name
550 matching_file_cache["path:" + fn] = sf
551 # Only allow eligability for filename/sha matching
552 # if there isn't a perfect path match.
553 if target_data.get(sf.name) is None:
554 matching_file_cache["file:" + fn.split("/")[-1]] = sf
555 matching_file_cache["sha:" + sf.sha1] = sf
556
Doug Zongkereef39442009-04-02 12:14:19 -0700557 for fn in sorted(target_data.keys()):
558 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700559 assert fn == tf.name
Michael Runge37335b42013-11-06 17:59:29 -0800560 sf = ClosestFileMatch(tf, matching_file_cache, renames)
561 if sf is not None and sf.name != tf.name:
562 print "File has moved from " + sf.name + " to " + tf.name
563 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700564
565 if sf is None or fn in OPTIONS.require_verbatim:
566 # This file should be included verbatim
567 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700568 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700569 print "send", fn, "verbatim"
570 tf.AddToZip(output_zip)
571 verbatim_targets.append((fn, tf.size))
572 elif tf.sha1 != sf.sha1:
573 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700574 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700575 else:
Michael Runge37335b42013-11-06 17:59:29 -0800576 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700577 pass
578
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700579 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700580
581 for diff in diffs:
582 tf, sf, d = diff.GetPatch()
583 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
584 # patch is almost as big as the file; don't bother patching
585 tf.AddToZip(output_zip)
586 verbatim_targets.append((tf.name, tf.size))
587 else:
Michael Runge37335b42013-11-06 17:59:29 -0800588 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
589 patch_list.append((sf.name, tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700590 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700591
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700592 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
593 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700594 metadata["pre-build"] = source_fp
595 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700596
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700597 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700598 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700599
Doug Zongker55d93282011-01-25 17:03:34 -0800600 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700601 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
602 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800603 target_boot = common.GetBootableImage(
604 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker5da317e2009-06-02 13:38:17 -0700605 updating_boot = (source_boot.data != target_boot.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700606
Doug Zongker55d93282011-01-25 17:03:34 -0800607 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700608 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
609 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800610 target_recovery = common.GetBootableImage(
611 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700612 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700613
Doug Zongker881dd402009-09-20 14:03:55 -0700614 # Here's how we divide up the progress bar:
615 # 0.1 for verifying the start state (PatchCheck calls)
616 # 0.8 for applying patches (ApplyPatch calls)
617 # 0.1 for unpacking verbatim files, symlinking, and doing the
618 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700619
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700620 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700621 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700622
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700623 script.Print("Verifying current system...")
624
Doug Zongkere5ff5902012-01-17 10:55:37 -0800625 device_specific.IncrementalOTA_VerifyBegin()
626
Doug Zongker881dd402009-09-20 14:03:55 -0700627 script.ShowProgress(0.1, 0)
628 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
629 if updating_boot:
630 total_verify_size += source_boot.size
631 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700632
Doug Zongker5a482092010-02-17 16:09:18 -0800633 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700634 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700635 so_far += sf.size
636 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700637
Doug Zongker5da317e2009-06-02 13:38:17 -0700638 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700639 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700640 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700641 print "boot target: %d source: %d diff: %d" % (
642 target_boot.size, source_boot.size, len(d))
643
Doug Zongker048e7ca2009-06-15 14:31:53 -0700644 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700645
Doug Zongker96a57e72010-09-26 14:57:41 -0700646 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700647
648 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
649 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700650 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700651 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700652 so_far += source_boot.size
653 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700654
655 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700656 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800657
Doug Zongker05d3dea2009-06-22 11:32:31 -0700658 device_specific.IncrementalOTA_VerifyEnd()
659
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700660 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700661
Doug Zongkere5ff5902012-01-17 10:55:37 -0800662 device_specific.IncrementalOTA_InstallBegin()
663
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700664 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700665 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700666 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700667
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700668 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700669 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
670 ["/"+i for i in sorted(source_data)
Michael Runge37335b42013-11-06 17:59:29 -0800671 if i not in target_data and
672 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700673 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700674
Doug Zongker881dd402009-09-20 14:03:55 -0700675 script.ShowProgress(0.8, 0)
676 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
677 if updating_boot:
678 total_patch_size += target_boot.size
679 so_far = 0
680
681 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700682 deferred_patch_list = []
683 for item in patch_list:
684 fn, tf, sf, size, _ = item
685 if tf.name == "system/build.prop":
686 deferred_patch_list.append(item)
687 continue
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800688 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700689 so_far += tf.size
690 script.SetProgress(so_far / total_patch_size)
691
Doug Zongkereef39442009-04-02 12:14:19 -0700692 if updating_boot:
Doug Zongker5da317e2009-06-02 13:38:17 -0700693 # Produce the boot image by applying a patch to the current
694 # contents of the boot partition, and write it back to the
695 # partition.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700696 script.Print("Patching boot image...")
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700697 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
698 % (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700699 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700700 target_boot.size, target_boot.sha1),
701 "-",
702 target_boot.size, target_boot.sha1,
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800703 source_boot.sha1, "patch/boot.img.p")
Doug Zongker881dd402009-09-20 14:03:55 -0700704 so_far += target_boot.size
705 script.SetProgress(so_far / total_patch_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700706 print "boot image changed; including."
707 else:
708 print "boot image unchanged; skipping."
709
710 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700711 # Recovery is generated as a patch using both the boot image
712 # (which contains the same linux kernel as recovery) and the file
713 # /system/etc/recovery-resource.dat (which contains all the images
714 # used in the recovery UI) as sources. This lets us minimize the
715 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700716 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700717 # For older builds where recovery-resource.dat is not present, we
718 # use only the boot image as the source.
719
720 MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
721 target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800722 script.DeleteFiles(["/system/recovery-from-boot.p",
723 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700724 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700725 else:
726 print "recovery image unchanged; skipping."
727
Doug Zongker881dd402009-09-20 14:03:55 -0700728 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700729
Doug Zongker1807e702012-02-28 12:21:08 -0800730 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700731
732 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700733 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700734 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700735 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700736
737 # Note that this call will mess up the tree of Items, so make sure
738 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800739 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700740 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
741
742 # Delete all the symlinks in source that aren't in target. This
743 # needs to happen before verbatim files are unpacked, in case a
744 # symlink in the source is replaced by a real file in the target.
745 to_delete = []
746 for dest, link in source_symlinks:
747 if link not in target_symlinks_d:
748 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700749 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700750
751 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700752 script.Print("Unpacking new files...")
753 script.UnpackPackageDir("system", "/system")
754
Doug Zongker42265392010-02-12 10:21:00 -0800755 if updating_recovery:
756 script.Print("Unpacking new recovery...")
757 script.UnpackPackageDir("recovery", "/system")
758
Michael Runge37335b42013-11-06 17:59:29 -0800759 if len(renames) > 0:
760 script.Print("Renaming files...")
761
762 for src in renames:
763 print "Renaming " + src + " to " + renames[src].name
764 script.RenameFile(src, renames[src].name)
765
Doug Zongker05d3dea2009-06-22 11:32:31 -0700766 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700767
768 # Create all the symlinks that don't already exist, or point to
769 # somewhere different than what we want. Delete each symlink before
770 # creating it, since the 'symlink' command won't overwrite.
771 to_create = []
772 for dest, link in target_symlinks:
773 if link in source_symlinks_d:
774 if dest != source_symlinks_d[link]:
775 to_create.append((dest, link))
776 else:
777 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700778 script.DeleteFiles([i[1] for i in to_create])
779 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700780
781 # Now that the symlinks are created, we can set all the
782 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700783 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700784
Doug Zongker881dd402009-09-20 14:03:55 -0700785 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700786 device_specific.IncrementalOTA_InstallEnd()
787
Doug Zongker1c390a22009-05-14 19:06:36 -0700788 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700789 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700790
Doug Zongkere92f15a2011-08-26 13:46:40 -0700791 # Patch the build.prop file last, so if something fails but the
792 # device can still come up, it appears to be the old build and will
793 # get set the OTA package again to retry.
794 script.Print("Patching remaining system files...")
795 for item in deferred_patch_list:
796 fn, tf, sf, size, _ = item
797 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700798 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700799
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700800 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700801 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700802
803
804def main(argv):
805
806 def option_handler(o, a):
807 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700808 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700809 elif o in ("-k", "--package_key"):
810 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700811 elif o in ("-i", "--incremental_from"):
812 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700813 elif o in ("-w", "--wipe_user_data"):
814 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700815 elif o in ("-n", "--no_prereq"):
816 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700817 elif o in ("-e", "--extra_script"):
818 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700819 elif o in ("-a", "--aslr_mode"):
820 if a in ("on", "On", "true", "True", "yes", "Yes"):
821 OPTIONS.aslr_mode = True
822 else:
823 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700824 elif o in ("--worker_threads"):
825 OPTIONS.worker_threads = int(a)
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900826 elif o in ("--no_signing"):
827 OPTIONS.no_signing = True
Doug Zongkereef39442009-04-02 12:14:19 -0700828 else:
829 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700830 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700831
832 args = common.ParseOptions(argv, __doc__,
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700833 extra_opts="b:k:i:d:wne:a:",
Doug Zongkereef39442009-04-02 12:14:19 -0700834 extra_long_opts=["board_config=",
835 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700836 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700837 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700838 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700839 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700840 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700841 "aslr_mode=",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900842 "no_signing",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700843 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700844 extra_option_handler=option_handler)
845
846 if len(args) != 2:
847 common.Usage(__doc__)
848 sys.exit(1)
849
Doug Zongker1c390a22009-05-14 19:06:36 -0700850 if OPTIONS.extra_script is not None:
851 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
852
Doug Zongkereef39442009-04-02 12:14:19 -0700853 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800854 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700855
Doug Zongkereef39442009-04-02 12:14:19 -0700856 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -0700857 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -0700858
859 # If this image was originally labelled with SELinux contexts, make sure we
860 # also apply the labels in our new image. During building, the "file_contexts"
861 # is in the out/ directory tree, but for repacking from target-files.zip it's
862 # in the root directory of the ramdisk.
863 if "selinux_fc" in OPTIONS.info_dict:
864 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
865 "file_contexts")
866
Doug Zongker37974732010-09-16 17:44:38 -0700867 if OPTIONS.verbose:
868 print "--- target info ---"
869 common.DumpInfoDict(OPTIONS.info_dict)
870
871 if OPTIONS.device_specific is None:
872 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
873 if OPTIONS.device_specific is not None:
874 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
875 print "using device-specific extensions in", OPTIONS.device_specific
876
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900877 if OPTIONS.no_signing:
878 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
879 else:
880 temp_zip_file = tempfile.NamedTemporaryFile()
881 output_zip = zipfile.ZipFile(temp_zip_file, "w",
882 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -0700883
884 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700885 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700886 if OPTIONS.package_key is None:
887 OPTIONS.package_key = OPTIONS.info_dict.get(
888 "default_system_dev_certificate",
889 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -0700890 else:
891 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800892 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -0700893 OPTIONS.target_info_dict = OPTIONS.info_dict
894 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700895 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -0700896 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700897 "default_system_dev_certificate",
898 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -0700899 if OPTIONS.verbose:
900 print "--- source info ---"
901 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700902 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700903
904 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700905
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900906 if not OPTIONS.no_signing:
907 SignOutput(temp_zip_file.name, args[1])
908 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700909
910 common.Cleanup()
911
912 print "done."
913
914
915if __name__ == '__main__':
916 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -0800917 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -0700918 main(sys.argv[1:])
919 except common.ExternalError, e:
920 print
921 print " ERROR: %s" % (e,)
922 print
923 sys.exit(1)