blob: a31d70aaf01c799acb8407070dc132d716f18811 [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 Zongker9b23f2c2013-11-25 14:44:12 -080055 -2 (--two_step)
56 Generate a 'two-step' OTA package, where recovery is updated
57 first, so that any changes made to the system partition are done
58 using the new recovery (new kernel, etc.).
59
Doug Zongkereef39442009-04-02 12:14:19 -070060"""
61
62import sys
63
64if sys.hexversion < 0x02040000:
65 print >> sys.stderr, "Python 2.4 or newer is required."
66 sys.exit(1)
67
68import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070069import errno
Doug Zongkereef39442009-04-02 12:14:19 -070070import os
71import re
Doug Zongkereef39442009-04-02 12:14:19 -070072import subprocess
73import tempfile
74import time
75import zipfile
76
davidcad0bb92011-03-15 14:21:38 +000077try:
78 from hashlib import sha1 as sha1
79except ImportError:
80 from sha import sha as sha1
81
Doug Zongkereef39442009-04-02 12:14:19 -070082import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070083import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070084
85OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -070086OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -070087OPTIONS.incremental_source = None
88OPTIONS.require_verbatim = set()
89OPTIONS.prohibit_verbatim = set(("system/build.prop",))
90OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070091OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070092OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070093OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070094OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070095OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -080096OPTIONS.two_step = False
Doug Zongkereef39442009-04-02 12:14:19 -070097
98def MostPopularKey(d, default):
99 """Given a dict, return the key corresponding to the largest
100 value. Returns 'default' if the dict is empty."""
101 x = [(v, k) for (k, v) in d.iteritems()]
102 if not x: return default
103 x.sort()
104 return x[-1][1]
105
106
107def IsSymlink(info):
108 """Return true if the zipfile.ZipInfo object passed in represents a
109 symlink."""
110 return (info.external_attr >> 16) == 0120777
111
Hristo Bojinov96be7202010-08-02 10:26:17 -0700112def IsRegular(info):
113 """Return true if the zipfile.ZipInfo object passed in represents a
114 symlink."""
115 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700116
Michael Runge4038aa82013-12-13 18:06:28 -0800117def ClosestFileMatch(src, tgtfiles, existing):
118 """Returns the closest file match between a source file and list
119 of potential matches. The exact filename match is preferred,
120 then the sha1 is searched for, and finally a file with the same
121 basename is evaluated. Rename support in the updater-binary is
122 required for the latter checks to be used."""
123
124 result = tgtfiles.get("path:" + src.name)
125 if result is not None:
126 return result
127
128 if not OPTIONS.target_info_dict.get("update_rename_support", False):
129 return None
130
131 if src.size < 1000:
132 return None
133
134 result = tgtfiles.get("sha1:" + src.sha1)
135 if result is not None and existing.get(result.name) is None:
136 return result
137 result = tgtfiles.get("file:" + src.name.split("/")[-1])
138 if result is not None and existing.get(result.name) is None:
139 return result
140 return None
141
Doug Zongkereef39442009-04-02 12:14:19 -0700142class Item:
143 """Items represent the metadata (user, group, mode) of files and
144 directories in the system image."""
145 ITEMS = {}
146 def __init__(self, name, dir=False):
147 self.name = name
148 self.uid = None
149 self.gid = None
150 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700151 self.selabel = None
152 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700153 self.dir = dir
154
155 if name:
156 self.parent = Item.Get(os.path.dirname(name), dir=True)
157 self.parent.children.append(self)
158 else:
159 self.parent = None
160 if dir:
161 self.children = []
162
163 def Dump(self, indent=0):
164 if self.uid is not None:
165 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
166 else:
167 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
168 if self.dir:
169 print "%s%s" % (" "*indent, self.descendants)
170 print "%s%s" % (" "*indent, self.best_subtree)
171 for i in self.children:
172 i.Dump(indent=indent+1)
173
174 @classmethod
175 def Get(cls, name, dir=False):
176 if name not in cls.ITEMS:
177 cls.ITEMS[name] = Item(name, dir=dir)
178 return cls.ITEMS[name]
179
180 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700181 def GetMetadata(cls, input_zip):
182
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700183 # The target_files contains a record of what the uid,
184 # gid, and mode are supposed to be.
185 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700186
187 for line in output.split("\n"):
188 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700189 columns = line.split()
190 name, uid, gid, mode = columns[:4]
191 selabel = None
192 capabilities = None
193
194 # After the first 4 columns, there are a series of key=value
195 # pairs. Extract out the fields we care about.
196 for element in columns[4:]:
197 key, value = element.split("=")
198 if key == "selabel":
199 selabel = value
200 if key == "capabilities":
201 capabilities = value
202
Doug Zongker283e2a12010-03-15 17:52:32 -0700203 i = cls.ITEMS.get(name, None)
204 if i is not None:
205 i.uid = int(uid)
206 i.gid = int(gid)
207 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700208 i.selabel = selabel
209 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700210 if i.dir:
211 i.children.sort(key=lambda i: i.name)
212
213 # set metadata for the files generated by this script.
214 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700215 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700216 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700217 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700218
219 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700220 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
221 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700222 set_perm to correctly chown/chmod all the files to their desired
223 values. Recursively calls itself for all descendants.
224
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700225 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700226 all descendants of this node. (dmode or fmode may be None.) Also
227 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700228 dmode, fmode, selabel, capabilities) tuple that will match the most
229 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700230 """
231
232 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700233 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700234 for i in self.children:
235 if i.dir:
236 for k, v in i.CountChildMetadata().iteritems():
237 d[k] = d.get(k, 0) + v
238 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700239 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700240 d[k] = d.get(k, 0) + 1
241
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700242 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
243 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700244
245 # First, find the (uid, gid) pair that matches the most
246 # descendants.
247 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700248 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700249 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
250 ug = MostPopularKey(ug, (0, 0))
251
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700252 # Now find the dmode, fmode, selabel, and capabilities that match
253 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700254 best_dmode = (0, 0755)
255 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700256 best_selabel = (0, None)
257 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700258 for k, count in d.iteritems():
259 if k[:2] != ug: continue
260 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
261 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700262 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
263 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
264 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700265
266 return d
267
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700268 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700269 """Append set_perm/set_perm_recursive commands to 'script' to
270 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700271 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700272
273 self.CountChildMetadata()
274
275 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700276 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700277 # item (and all its children) have already been set to. We only
278 # need to issue set_perm/set_perm_recursive commands if we're
279 # supposed to be something different.
280 if item.dir:
281 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700282 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700283 current = item.best_subtree
284
285 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700286 item.mode != current[2] or item.selabel != current[4] or \
287 item.capabilities != current[5]:
288 script.SetPermissions("/"+item.name, item.uid, item.gid,
289 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700290
291 for i in item.children:
292 recurse(i, current)
293 else:
294 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700295 item.mode != current[3] or item.selabel != current[4] or \
296 item.capabilities != current[5]:
297 script.SetPermissions("/"+item.name, item.uid, item.gid,
298 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700299
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700300 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700301
302
303def CopySystemFiles(input_zip, output_zip=None,
304 substitute=None):
305 """Copies files underneath system/ in the input zip to the output
306 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800307 list of symlinks. output_zip may be None, in which case the copy is
308 skipped (but the other side effects still happen). substitute is an
309 optional dict of {output filename: contents} to be output instead of
310 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700311 """
312
313 symlinks = []
314
315 for info in input_zip.infolist():
316 if info.filename.startswith("SYSTEM/"):
317 basefilename = info.filename[7:]
318 if IsSymlink(info):
319 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700320 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700321 else:
322 info2 = copy.copy(info)
323 fn = info2.filename = "system/" + basefilename
324 if substitute and fn in substitute and substitute[fn] is None:
325 continue
326 if output_zip is not None:
327 if substitute and fn in substitute:
328 data = substitute[fn]
329 else:
330 data = input_zip.read(info.filename)
331 output_zip.writestr(info2, data)
332 if fn.endswith("/"):
333 Item.Get(fn[:-1], dir=True)
334 else:
335 Item.Get(fn, dir=False)
336
337 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800338 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700339
340
Doug Zongkereef39442009-04-02 12:14:19 -0700341def SignOutput(temp_zip_name, output_zip_name):
342 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
343 pw = key_passwords[OPTIONS.package_key]
344
Doug Zongker951495f2009-08-14 12:44:19 -0700345 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
346 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700347
348
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700349def AppendAssertions(script, info_dict):
350 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700351 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700352
Doug Zongkereef39442009-04-02 12:14:19 -0700353
Doug Zongkerb32161a2012-08-21 10:33:44 -0700354def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700355 """Generate a binary patch that creates the recovery image starting
356 with the boot image. (Most of the space in these images is just the
357 kernel, which is identical for the two, so the resulting patch
358 should be efficient.) Add it to the output zip, along with a shell
359 script that is run from init.rc on first boot to actually do the
360 patching and install the new recovery image.
361
362 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700363 corresponding images. info should be the dictionary returned by
364 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700365
366 Returns an Item for the shell script, which must be made
367 executable.
368 """
369
Doug Zongkerb32161a2012-08-21 10:33:44 -0700370 diff_program = ["imgdiff"]
371 path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
372 if os.path.exists(path):
373 diff_program.append("-b")
374 diff_program.append(path)
375 bonus_args = "-b /system/etc/recovery-resource.dat"
376 else:
377 bonus_args = ""
378
379 d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
Doug Zongker761e6422009-09-25 10:45:39 -0700380 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700381 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700382 Item.Get("system/recovery-from-boot.p", dir=False)
383
Doug Zongker96a57e72010-09-26 14:57:41 -0700384 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
385 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700386
Doug Zongker73ef8252009-07-23 15:12:53 -0700387 sh = """#!/system/bin/sh
Doug Zongker0276d182011-12-02 10:46:59 -0800388if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700389 log -t recovery "Installing new recovery image"
Doug Zongkerb32161a2012-08-21 10:33:44 -0700390 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 -0700391else
392 log -t recovery "Recovery image already installed"
393fi
394""" % { 'boot_size': boot_img.size,
395 'boot_sha1': boot_img.sha1,
Doug Zongker73ef8252009-07-23 15:12:53 -0700396 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700397 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700398 'boot_type': boot_type,
399 'boot_device': boot_device,
400 'recovery_type': recovery_type,
401 'recovery_device': recovery_device,
Doug Zongkerb32161a2012-08-21 10:33:44 -0700402 'bonus_args': bonus_args,
Doug Zongker67369982010-07-07 13:53:32 -0700403 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700404 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700405 return Item.Get("system/etc/install-recovery.sh", dir=False)
406
407
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700408def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700409 # TODO: how to determine this? We don't know what version it will
410 # be installed on top of. For now, we expect the API just won't
411 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700412 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700413
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700414 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
415 OPTIONS.info_dict),
416 "pre-device": GetBuildProp("ro.product.device",
417 OPTIONS.info_dict),
418 "post-timestamp": GetBuildProp("ro.build.date.utc",
419 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700420 }
421
Doug Zongker05d3dea2009-06-22 11:32:31 -0700422 device_specific = common.DeviceSpecificParams(
423 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700424 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700425 output_zip=output_zip,
426 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700427 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700428 metadata=metadata,
429 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700430
Doug Zongker962069c2009-04-23 11:41:58 -0700431 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700432 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700433 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
434 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700435
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700436 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700437 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800438
439 # Two-step package strategy (in chronological order, which is *not*
440 # the order in which the generated script has things):
441 #
442 # if stage is not "2/3" or "3/3":
443 # write recovery image to boot partition
444 # set stage to "2/3"
445 # reboot to boot partition and restart recovery
446 # else if stage is "2/3":
447 # write recovery image to recovery partition
448 # set stage to "3/3"
449 # reboot to recovery partition and restart recovery
450 # else:
451 # (stage must be "3/3")
452 # set stage to ""
453 # do normal full package installation:
454 # wipe and install system, boot image, etc.
455 # set up system to update recovery partition on first boot
456 # complete script normally (allow recovery to mark itself finished and reboot)
457
458 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
459 OPTIONS.input_tmp, "RECOVERY")
460 if OPTIONS.two_step:
461 if not OPTIONS.info_dict.get("multistage_support", None):
462 assert False, "two-step packages not supported by this build"
463 fs = OPTIONS.info_dict["fstab"]["/misc"]
464 assert fs.fs_type.upper() == "EMMC", \
465 "two-step packages only supported on devices with EMMC /misc partitions"
466 bcb_dev = {"bcb_dev": fs.device}
467 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
468 script.AppendExtra("""
469if get_stage("%(bcb_dev)s", "stage") == "2/3" then
470""" % bcb_dev)
471 script.WriteRawImage("/recovery", "recovery.img")
472 script.AppendExtra("""
473set_stage("%(bcb_dev)s", "3/3");
474reboot_now("%(bcb_dev)s", "recovery");
475else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
476""" % bcb_dev)
477
Doug Zongkere5ff5902012-01-17 10:55:37 -0800478 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700479
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700480 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700481
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700482 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700483 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700484
Kenny Rootf32dc712012-04-08 10:42:34 -0700485 if "selinux_fc" in OPTIONS.info_dict:
486 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500487
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700488 script.FormatPartition("/system")
489 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700490 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700491 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700492
Doug Zongker1807e702012-02-28 12:21:08 -0800493 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700494 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700495
Doug Zongker55d93282011-01-25 17:03:34 -0800496 boot_img = common.GetBootableImage("boot.img", "boot.img",
497 OPTIONS.input_tmp, "BOOT")
Doug Zongkerb32161a2012-08-21 10:33:44 -0700498 MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700499
Doug Zongker283e2a12010-03-15 17:52:32 -0700500 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700501 Item.Get("system").SetPermissions(script)
502
Doug Zongker37974732010-09-16 17:44:38 -0700503 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700504 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700505 script.ShowProgress(0.2, 0)
506
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700507 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700508 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700509
510 script.ShowProgress(0.1, 0)
511 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700512
Doug Zongker1c390a22009-05-14 19:06:36 -0700513 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700514 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700515
Doug Zongker14833602010-02-02 13:12:04 -0800516 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800517
518 if OPTIONS.two_step:
519 script.AppendExtra("""
520set_stage("%(bcb_dev)s", "");
521""" % bcb_dev)
522 script.AppendExtra("else\n")
523 script.WriteRawImage("/boot", "recovery.img")
524 script.AppendExtra("""
525set_stage("%(bcb_dev)s", "2/3");
526reboot_now("%(bcb_dev)s", "");
527endif;
528endif;
529""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700530 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700531 WriteMetadata(metadata, output_zip)
532
Stephen Smalley56882bf2012-02-09 13:36:21 -0500533def WritePolicyConfig(file_context, output_zip):
534 f = open(file_context, 'r');
535 basename = os.path.basename(file_context)
536 common.ZipWriteStr(output_zip, basename, f.read())
537
Doug Zongker2ea21062010-04-28 16:05:21 -0700538
539def WriteMetadata(metadata, output_zip):
540 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
541 "".join(["%s=%s\n" % kv
542 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700543
Doug Zongkereef39442009-04-02 12:14:19 -0700544def LoadSystemFiles(z):
545 """Load all the files from SYSTEM/... in a given target-files
546 ZipFile, and return a dict of {filename: File object}."""
547 out = {}
548 for info in z.infolist():
549 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700550 basefilename = info.filename[7:]
551 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700552 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700553 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800554 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700555
556
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700557def GetBuildProp(prop, info_dict):
558 """Return the fingerprint of the build of a given target-files info_dict."""
559 try:
560 return info_dict.get("build.prop", {})[prop]
561 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700562 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700563
Michael Runge4038aa82013-12-13 18:06:28 -0800564def AddToKnownPaths(filename, known_paths):
565 if filename[-1] == "/":
566 return
567 dirs = filename.split("/")[:-1]
568 while len(dirs) > 0:
569 path = "/".join(dirs)
570 if path in known_paths:
571 break;
572 known_paths.add(path)
573 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700574
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700575def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700576 source_version = OPTIONS.source_info_dict["recovery_api_version"]
577 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700578
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700579 if source_version == 0:
580 print ("WARNING: generating edify script for a source that "
581 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700582 script = edify_generator.EdifyGenerator(source_version,
583 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700584
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700585 metadata = {"pre-device": GetBuildProp("ro.product.device",
586 OPTIONS.source_info_dict),
587 "post-timestamp": GetBuildProp("ro.build.date.utc",
588 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700589 }
590
Doug Zongker05d3dea2009-06-22 11:32:31 -0700591 device_specific = common.DeviceSpecificParams(
592 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800593 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700594 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800595 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700596 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700597 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700598 metadata=metadata,
599 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700600
Doug Zongkereef39442009-04-02 12:14:19 -0700601 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800602 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700603 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800604 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700605
606 verbatim_targets = []
607 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700608 diffs = []
Michael Runge4038aa82013-12-13 18:06:28 -0800609 renames = {}
610 known_paths = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700611 largest_source_size = 0
Michael Runge4038aa82013-12-13 18:06:28 -0800612
613 matching_file_cache = {}
614 for fn, sf in source_data.items():
615 assert fn == sf.name
616 matching_file_cache["path:" + fn] = sf
617 if fn in target_data.keys():
618 AddToKnownPaths(fn, known_paths)
619 # Only allow eligibility for filename/sha matching
620 # if there isn't a perfect path match.
621 if target_data.get(sf.name) is None:
622 matching_file_cache["file:" + fn.split("/")[-1]] = sf
623 matching_file_cache["sha:" + sf.sha1] = sf
624
Doug Zongkereef39442009-04-02 12:14:19 -0700625 for fn in sorted(target_data.keys()):
626 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700627 assert fn == tf.name
Michael Runge4038aa82013-12-13 18:06:28 -0800628 sf = ClosestFileMatch(tf, matching_file_cache, renames)
629 if sf is not None and sf.name != tf.name:
630 print "File has moved from " + sf.name + " to " + tf.name
631 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700632
633 if sf is None or fn in OPTIONS.require_verbatim:
634 # This file should be included verbatim
635 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700636 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700637 print "send", fn, "verbatim"
638 tf.AddToZip(output_zip)
639 verbatim_targets.append((fn, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800640 if fn in target_data.keys():
641 AddToKnownPaths(fn, known_paths)
Doug Zongkereef39442009-04-02 12:14:19 -0700642 elif tf.sha1 != sf.sha1:
643 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700644 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700645 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800646 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700647 pass
648
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700649 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700650
651 for diff in diffs:
652 tf, sf, d = diff.GetPatch()
Michael Runge4038aa82013-12-13 18:06:28 -0800653 path = "/".join(tf.name.split("/")[:-1])
654 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
655 path not in known_paths:
Doug Zongker761e6422009-09-25 10:45:39 -0700656 # patch is almost as big as the file; don't bother patching
Michael Runge4038aa82013-12-13 18:06:28 -0800657 # or a patch + rename cannot take place due to the target
658 # directory not existing
Doug Zongker761e6422009-09-25 10:45:39 -0700659 tf.AddToZip(output_zip)
660 verbatim_targets.append((tf.name, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800661 if sf.name in renames:
662 del renames[sf.name]
663 AddToKnownPaths(tf.name, known_paths)
Doug Zongker761e6422009-09-25 10:45:39 -0700664 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800665 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
666 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700667 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700668
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700669 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
670 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700671 metadata["pre-build"] = source_fp
672 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700673
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700674 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700675 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700676
Doug Zongker55d93282011-01-25 17:03:34 -0800677 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700678 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
679 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800680 target_boot = common.GetBootableImage(
681 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800682 updating_boot = (not OPTIONS.two_step and
683 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700684
Doug Zongker55d93282011-01-25 17:03:34 -0800685 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700686 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
687 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800688 target_recovery = common.GetBootableImage(
689 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700690 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700691
Doug Zongker881dd402009-09-20 14:03:55 -0700692 # Here's how we divide up the progress bar:
693 # 0.1 for verifying the start state (PatchCheck calls)
694 # 0.8 for applying patches (ApplyPatch calls)
695 # 0.1 for unpacking verbatim files, symlinking, and doing the
696 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700697
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700698 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700699 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700700
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800701 # Two-step incremental package strategy (in chronological order,
702 # which is *not* the order in which the generated script has
703 # things):
704 #
705 # if stage is not "2/3" or "3/3":
706 # do verification on current system
707 # write recovery image to boot partition
708 # set stage to "2/3"
709 # reboot to boot partition and restart recovery
710 # else if stage is "2/3":
711 # write recovery image to recovery partition
712 # set stage to "3/3"
713 # reboot to recovery partition and restart recovery
714 # else:
715 # (stage must be "3/3")
716 # perform update:
717 # patch system files, etc.
718 # force full install of new boot image
719 # set up system to update recovery partition on first boot
720 # complete script normally (allow recovery to mark itself finished and reboot)
721
722 if OPTIONS.two_step:
723 if not OPTIONS.info_dict.get("multistage_support", None):
724 assert False, "two-step packages not supported by this build"
725 fs = OPTIONS.info_dict["fstab"]["/misc"]
726 assert fs.fs_type.upper() == "EMMC", \
727 "two-step packages only supported on devices with EMMC /misc partitions"
728 bcb_dev = {"bcb_dev": fs.device}
729 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
730 script.AppendExtra("""
731if get_stage("%(bcb_dev)s", "stage") == "2/3" then
732""" % bcb_dev)
733 script.AppendExtra("sleep(20);\n");
734 script.WriteRawImage("/recovery", "recovery.img")
735 script.AppendExtra("""
736set_stage("%(bcb_dev)s", "3/3");
737reboot_now("%(bcb_dev)s", "recovery");
738else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
739""" % bcb_dev)
740
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700741 script.Print("Verifying current system...")
742
Doug Zongkere5ff5902012-01-17 10:55:37 -0800743 device_specific.IncrementalOTA_VerifyBegin()
744
Doug Zongker881dd402009-09-20 14:03:55 -0700745 script.ShowProgress(0.1, 0)
Michael Runge4038aa82013-12-13 18:06:28 -0800746 total_verify_size = float(sum([i[1].size for i in patch_list]) + 1)
Doug Zongker881dd402009-09-20 14:03:55 -0700747 if updating_boot:
748 total_verify_size += source_boot.size
749 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700750
Michael Runge4038aa82013-12-13 18:06:28 -0800751 for tf, sf, size, patch_sha in patch_list:
752 if tf.name != sf.name:
753 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
754 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700755 so_far += sf.size
756 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700757
Doug Zongker5da317e2009-06-02 13:38:17 -0700758 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700759 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700760 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700761 print "boot target: %d source: %d diff: %d" % (
762 target_boot.size, source_boot.size, len(d))
763
Doug Zongker048e7ca2009-06-15 14:31:53 -0700764 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700765
Doug Zongker96a57e72010-09-26 14:57:41 -0700766 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700767
768 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
769 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700770 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700771 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700772 so_far += source_boot.size
773 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700774
775 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700776 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800777
Doug Zongker05d3dea2009-06-22 11:32:31 -0700778 device_specific.IncrementalOTA_VerifyEnd()
779
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800780 if OPTIONS.two_step:
781 script.WriteRawImage("/boot", "recovery.img")
782 script.AppendExtra("""
783set_stage("%(bcb_dev)s", "2/3");
784reboot_now("%(bcb_dev)s", "");
785else
786""" % bcb_dev)
787
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700788 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700789
Doug Zongkere5ff5902012-01-17 10:55:37 -0800790 device_specific.IncrementalOTA_InstallBegin()
791
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800792 if OPTIONS.two_step:
793 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
794 script.WriteRawImage("/boot", "boot.img")
795 print "writing full boot image (forced by two-step mode)"
796
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700797 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700798 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700799 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700800
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700801 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700802 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
803 ["/"+i for i in sorted(source_data)
Michael Runge4038aa82013-12-13 18:06:28 -0800804 if i not in target_data and
805 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700806 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700807
Doug Zongker881dd402009-09-20 14:03:55 -0700808 script.ShowProgress(0.8, 0)
809 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
810 if updating_boot:
811 total_patch_size += target_boot.size
812 so_far = 0
813
814 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700815 deferred_patch_list = []
816 for item in patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800817 tf, sf, size, _ = item
Doug Zongkere92f15a2011-08-26 13:46:40 -0700818 if tf.name == "system/build.prop":
819 deferred_patch_list.append(item)
820 continue
Michael Runge4038aa82013-12-13 18:06:28 -0800821 if (sf.name != tf.name):
822 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
823 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700824 so_far += tf.size
825 script.SetProgress(so_far / total_patch_size)
826
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800827 if not OPTIONS.two_step:
828 if updating_boot:
829 # Produce the boot image by applying a patch to the current
830 # contents of the boot partition, and write it back to the
831 # partition.
832 script.Print("Patching boot image...")
833 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
834 % (boot_type, boot_device,
835 source_boot.size, source_boot.sha1,
836 target_boot.size, target_boot.sha1),
837 "-",
838 target_boot.size, target_boot.sha1,
839 source_boot.sha1, "patch/boot.img.p")
840 so_far += target_boot.size
841 script.SetProgress(so_far / total_patch_size)
842 print "boot image changed; including."
843 else:
844 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -0700845
846 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700847 # Recovery is generated as a patch using both the boot image
848 # (which contains the same linux kernel as recovery) and the file
849 # /system/etc/recovery-resource.dat (which contains all the images
850 # used in the recovery UI) as sources. This lets us minimize the
851 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700852 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700853 # For older builds where recovery-resource.dat is not present, we
854 # use only the boot image as the source.
855
856 MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
857 target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800858 script.DeleteFiles(["/system/recovery-from-boot.p",
859 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700860 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700861 else:
862 print "recovery image unchanged; skipping."
863
Doug Zongker881dd402009-09-20 14:03:55 -0700864 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700865
Doug Zongker1807e702012-02-28 12:21:08 -0800866 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700867
868 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700869 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700870 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700871 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700872
873 # Note that this call will mess up the tree of Items, so make sure
874 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800875 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700876 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
877
878 # Delete all the symlinks in source that aren't in target. This
879 # needs to happen before verbatim files are unpacked, in case a
880 # symlink in the source is replaced by a real file in the target.
881 to_delete = []
882 for dest, link in source_symlinks:
883 if link not in target_symlinks_d:
884 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700885 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700886
887 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700888 script.Print("Unpacking new files...")
889 script.UnpackPackageDir("system", "/system")
890
Doug Zongker42265392010-02-12 10:21:00 -0800891 if updating_recovery:
892 script.Print("Unpacking new recovery...")
893 script.UnpackPackageDir("recovery", "/system")
894
Michael Runge4038aa82013-12-13 18:06:28 -0800895 if len(renames) > 0:
896 script.Print("Renaming files...")
897
898 for src in renames:
899 print "Renaming " + src + " to " + renames[src].name
900 script.RenameFile(src, renames[src].name)
901
Doug Zongker05d3dea2009-06-22 11:32:31 -0700902 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700903
904 # Create all the symlinks that don't already exist, or point to
905 # somewhere different than what we want. Delete each symlink before
906 # creating it, since the 'symlink' command won't overwrite.
907 to_create = []
908 for dest, link in target_symlinks:
909 if link in source_symlinks_d:
910 if dest != source_symlinks_d[link]:
911 to_create.append((dest, link))
912 else:
913 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700914 script.DeleteFiles([i[1] for i in to_create])
915 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700916
917 # Now that the symlinks are created, we can set all the
918 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700919 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700920
Doug Zongker881dd402009-09-20 14:03:55 -0700921 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700922 device_specific.IncrementalOTA_InstallEnd()
923
Doug Zongker1c390a22009-05-14 19:06:36 -0700924 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700925 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700926
Doug Zongkere92f15a2011-08-26 13:46:40 -0700927 # Patch the build.prop file last, so if something fails but the
928 # device can still come up, it appears to be the old build and will
929 # get set the OTA package again to retry.
930 script.Print("Patching remaining system files...")
931 for item in deferred_patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800932 tf, sf, size, _ = item
933 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700934 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700935
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800936 if OPTIONS.two_step:
937 script.AppendExtra("""
938set_stage("%(bcb_dev)s", "");
939endif;
940endif;
941""" % bcb_dev)
942
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700943 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700944 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700945
946
947def main(argv):
948
949 def option_handler(o, a):
950 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700951 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700952 elif o in ("-k", "--package_key"):
953 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700954 elif o in ("-i", "--incremental_from"):
955 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700956 elif o in ("-w", "--wipe_user_data"):
957 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700958 elif o in ("-n", "--no_prereq"):
959 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700960 elif o in ("-e", "--extra_script"):
961 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700962 elif o in ("-a", "--aslr_mode"):
963 if a in ("on", "On", "true", "True", "yes", "Yes"):
964 OPTIONS.aslr_mode = True
965 else:
966 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700967 elif o in ("--worker_threads"):
968 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800969 elif o in ("-2", "--two_step"):
970 OPTIONS.two_step = True
Doug Zongkereef39442009-04-02 12:14:19 -0700971 else:
972 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700973 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700974
975 args = common.ParseOptions(argv, __doc__,
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800976 extra_opts="b:k:i:d:wne:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -0700977 extra_long_opts=["board_config=",
978 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700979 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700980 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700981 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700982 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700983 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700984 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800985 "two_step",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700986 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700987 extra_option_handler=option_handler)
988
989 if len(args) != 2:
990 common.Usage(__doc__)
991 sys.exit(1)
992
Doug Zongker1c390a22009-05-14 19:06:36 -0700993 if OPTIONS.extra_script is not None:
994 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
995
Doug Zongkereef39442009-04-02 12:14:19 -0700996 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800997 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700998
Doug Zongkereef39442009-04-02 12:14:19 -0700999 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001000 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001001
1002 # If this image was originally labelled with SELinux contexts, make sure we
1003 # also apply the labels in our new image. During building, the "file_contexts"
1004 # is in the out/ directory tree, but for repacking from target-files.zip it's
1005 # in the root directory of the ramdisk.
1006 if "selinux_fc" in OPTIONS.info_dict:
1007 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1008 "file_contexts")
1009
Doug Zongker37974732010-09-16 17:44:38 -07001010 if OPTIONS.verbose:
1011 print "--- target info ---"
1012 common.DumpInfoDict(OPTIONS.info_dict)
1013
1014 if OPTIONS.device_specific is None:
1015 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1016 if OPTIONS.device_specific is not None:
1017 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
1018 print "using device-specific extensions in", OPTIONS.device_specific
1019
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001020 temp_zip_file = tempfile.NamedTemporaryFile()
1021 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1022 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001023
1024 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001025 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001026 if OPTIONS.package_key is None:
1027 OPTIONS.package_key = OPTIONS.info_dict.get(
1028 "default_system_dev_certificate",
1029 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001030 else:
1031 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001032 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001033 OPTIONS.target_info_dict = OPTIONS.info_dict
1034 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001035 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001036 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001037 "default_system_dev_certificate",
1038 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001039 if OPTIONS.verbose:
1040 print "--- source info ---"
1041 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001042 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001043
1044 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001045
1046 SignOutput(temp_zip_file.name, args[1])
1047 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001048
1049 common.Cleanup()
1050
1051 print "done."
1052
1053
1054if __name__ == '__main__':
1055 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001056 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001057 main(sys.argv[1:])
1058 except common.ExternalError, e:
1059 print
1060 print " ERROR: %s" % (e,)
1061 print
1062 sys.exit(1)