blob: 28e8513dfb6f6b6288ac4aac3e0744243bdae19b [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
Doug Zongkereef39442009-04-02 12:14:19 -0700117class Item:
118 """Items represent the metadata (user, group, mode) of files and
119 directories in the system image."""
120 ITEMS = {}
121 def __init__(self, name, dir=False):
122 self.name = name
123 self.uid = None
124 self.gid = None
125 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700126 self.selabel = None
127 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700128 self.dir = dir
129
130 if name:
131 self.parent = Item.Get(os.path.dirname(name), dir=True)
132 self.parent.children.append(self)
133 else:
134 self.parent = None
135 if dir:
136 self.children = []
137
138 def Dump(self, indent=0):
139 if self.uid is not None:
140 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
141 else:
142 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
143 if self.dir:
144 print "%s%s" % (" "*indent, self.descendants)
145 print "%s%s" % (" "*indent, self.best_subtree)
146 for i in self.children:
147 i.Dump(indent=indent+1)
148
149 @classmethod
150 def Get(cls, name, dir=False):
151 if name not in cls.ITEMS:
152 cls.ITEMS[name] = Item(name, dir=dir)
153 return cls.ITEMS[name]
154
155 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700156 def GetMetadata(cls, input_zip):
157
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700158 # The target_files contains a record of what the uid,
159 # gid, and mode are supposed to be.
160 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700161
162 for line in output.split("\n"):
163 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700164 columns = line.split()
165 name, uid, gid, mode = columns[:4]
166 selabel = None
167 capabilities = None
168
169 # After the first 4 columns, there are a series of key=value
170 # pairs. Extract out the fields we care about.
171 for element in columns[4:]:
172 key, value = element.split("=")
173 if key == "selabel":
174 selabel = value
175 if key == "capabilities":
176 capabilities = value
177
Doug Zongker283e2a12010-03-15 17:52:32 -0700178 i = cls.ITEMS.get(name, None)
179 if i is not None:
180 i.uid = int(uid)
181 i.gid = int(gid)
182 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700183 i.selabel = selabel
184 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700185 if i.dir:
186 i.children.sort(key=lambda i: i.name)
187
188 # set metadata for the files generated by this script.
189 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700190 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700191 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700192 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700193
194 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700195 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
196 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700197 set_perm to correctly chown/chmod all the files to their desired
198 values. Recursively calls itself for all descendants.
199
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700200 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700201 all descendants of this node. (dmode or fmode may be None.) Also
202 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700203 dmode, fmode, selabel, capabilities) tuple that will match the most
204 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700205 """
206
207 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700208 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700209 for i in self.children:
210 if i.dir:
211 for k, v in i.CountChildMetadata().iteritems():
212 d[k] = d.get(k, 0) + v
213 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700214 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700215 d[k] = d.get(k, 0) + 1
216
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700217 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
218 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700219
220 # First, find the (uid, gid) pair that matches the most
221 # descendants.
222 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700223 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700224 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
225 ug = MostPopularKey(ug, (0, 0))
226
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700227 # Now find the dmode, fmode, selabel, and capabilities that match
228 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700229 best_dmode = (0, 0755)
230 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700231 best_selabel = (0, None)
232 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700233 for k, count in d.iteritems():
234 if k[:2] != ug: continue
235 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
236 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700237 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
238 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
239 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700240
241 return d
242
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700243 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700244 """Append set_perm/set_perm_recursive commands to 'script' to
245 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700246 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700247
248 self.CountChildMetadata()
249
250 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700251 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700252 # item (and all its children) have already been set to. We only
253 # need to issue set_perm/set_perm_recursive commands if we're
254 # supposed to be something different.
255 if item.dir:
256 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700257 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700258 current = item.best_subtree
259
260 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700261 item.mode != current[2] or item.selabel != current[4] or \
262 item.capabilities != current[5]:
263 script.SetPermissions("/"+item.name, item.uid, item.gid,
264 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700265
266 for i in item.children:
267 recurse(i, current)
268 else:
269 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700270 item.mode != current[3] or item.selabel != current[4] or \
271 item.capabilities != current[5]:
272 script.SetPermissions("/"+item.name, item.uid, item.gid,
273 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700274
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700275 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700276
277
278def CopySystemFiles(input_zip, output_zip=None,
279 substitute=None):
280 """Copies files underneath system/ in the input zip to the output
281 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800282 list of symlinks. output_zip may be None, in which case the copy is
283 skipped (but the other side effects still happen). substitute is an
284 optional dict of {output filename: contents} to be output instead of
285 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700286 """
287
288 symlinks = []
289
290 for info in input_zip.infolist():
291 if info.filename.startswith("SYSTEM/"):
292 basefilename = info.filename[7:]
293 if IsSymlink(info):
294 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700295 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700296 else:
297 info2 = copy.copy(info)
298 fn = info2.filename = "system/" + basefilename
299 if substitute and fn in substitute and substitute[fn] is None:
300 continue
301 if output_zip is not None:
302 if substitute and fn in substitute:
303 data = substitute[fn]
304 else:
305 data = input_zip.read(info.filename)
306 output_zip.writestr(info2, data)
307 if fn.endswith("/"):
308 Item.Get(fn[:-1], dir=True)
309 else:
310 Item.Get(fn, dir=False)
311
312 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800313 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700314
315
Doug Zongkereef39442009-04-02 12:14:19 -0700316def SignOutput(temp_zip_name, output_zip_name):
317 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
318 pw = key_passwords[OPTIONS.package_key]
319
Doug Zongker951495f2009-08-14 12:44:19 -0700320 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
321 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700322
323
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700324def AppendAssertions(script, info_dict):
325 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700326 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700327
Doug Zongkereef39442009-04-02 12:14:19 -0700328
Doug Zongkerb32161a2012-08-21 10:33:44 -0700329def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700330 """Generate a binary patch that creates the recovery image starting
331 with the boot image. (Most of the space in these images is just the
332 kernel, which is identical for the two, so the resulting patch
333 should be efficient.) Add it to the output zip, along with a shell
334 script that is run from init.rc on first boot to actually do the
335 patching and install the new recovery image.
336
337 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700338 corresponding images. info should be the dictionary returned by
339 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700340
341 Returns an Item for the shell script, which must be made
342 executable.
343 """
344
Doug Zongkerb32161a2012-08-21 10:33:44 -0700345 diff_program = ["imgdiff"]
346 path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
347 if os.path.exists(path):
348 diff_program.append("-b")
349 diff_program.append(path)
350 bonus_args = "-b /system/etc/recovery-resource.dat"
351 else:
352 bonus_args = ""
353
354 d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
Doug Zongker761e6422009-09-25 10:45:39 -0700355 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700356 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700357 Item.Get("system/recovery-from-boot.p", dir=False)
358
Doug Zongker96a57e72010-09-26 14:57:41 -0700359 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
360 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700361
Doug Zongker73ef8252009-07-23 15:12:53 -0700362 sh = """#!/system/bin/sh
Doug Zongker0276d182011-12-02 10:46:59 -0800363if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700364 log -t recovery "Installing new recovery image"
Doug Zongkerb32161a2012-08-21 10:33:44 -0700365 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 -0700366else
367 log -t recovery "Recovery image already installed"
368fi
369""" % { 'boot_size': boot_img.size,
370 'boot_sha1': boot_img.sha1,
Doug Zongker73ef8252009-07-23 15:12:53 -0700371 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700372 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700373 'boot_type': boot_type,
374 'boot_device': boot_device,
375 'recovery_type': recovery_type,
376 'recovery_device': recovery_device,
Doug Zongkerb32161a2012-08-21 10:33:44 -0700377 'bonus_args': bonus_args,
Doug Zongker67369982010-07-07 13:53:32 -0700378 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700379 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700380 return Item.Get("system/etc/install-recovery.sh", dir=False)
381
382
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700383def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700384 # TODO: how to determine this? We don't know what version it will
385 # be installed on top of. For now, we expect the API just won't
386 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700387 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700388
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700389 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
390 OPTIONS.info_dict),
391 "pre-device": GetBuildProp("ro.product.device",
392 OPTIONS.info_dict),
393 "post-timestamp": GetBuildProp("ro.build.date.utc",
394 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700395 }
396
Doug Zongker05d3dea2009-06-22 11:32:31 -0700397 device_specific = common.DeviceSpecificParams(
398 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700399 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700400 output_zip=output_zip,
401 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700402 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700403 metadata=metadata,
404 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700405
Doug Zongker962069c2009-04-23 11:41:58 -0700406 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700407 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700408 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
409 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700410
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700411 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700412 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800413
414 # Two-step package strategy (in chronological order, which is *not*
415 # the order in which the generated script has things):
416 #
417 # if stage is not "2/3" or "3/3":
418 # write recovery image to boot partition
419 # set stage to "2/3"
420 # reboot to boot partition and restart recovery
421 # else if stage is "2/3":
422 # write recovery image to recovery partition
423 # set stage to "3/3"
424 # reboot to recovery partition and restart recovery
425 # else:
426 # (stage must be "3/3")
427 # set stage to ""
428 # do normal full package installation:
429 # wipe and install system, boot image, etc.
430 # set up system to update recovery partition on first boot
431 # complete script normally (allow recovery to mark itself finished and reboot)
432
433 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
434 OPTIONS.input_tmp, "RECOVERY")
435 if OPTIONS.two_step:
436 if not OPTIONS.info_dict.get("multistage_support", None):
437 assert False, "two-step packages not supported by this build"
438 fs = OPTIONS.info_dict["fstab"]["/misc"]
439 assert fs.fs_type.upper() == "EMMC", \
440 "two-step packages only supported on devices with EMMC /misc partitions"
441 bcb_dev = {"bcb_dev": fs.device}
442 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
443 script.AppendExtra("""
444if get_stage("%(bcb_dev)s", "stage") == "2/3" then
445""" % bcb_dev)
446 script.WriteRawImage("/recovery", "recovery.img")
447 script.AppendExtra("""
448set_stage("%(bcb_dev)s", "3/3");
449reboot_now("%(bcb_dev)s", "recovery");
450else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
451""" % bcb_dev)
452
Doug Zongkere5ff5902012-01-17 10:55:37 -0800453 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700454
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700455 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700456
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700457 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700458 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700459
Kenny Rootf32dc712012-04-08 10:42:34 -0700460 if "selinux_fc" in OPTIONS.info_dict:
461 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500462
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700463 script.FormatPartition("/system")
464 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700465 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700466 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700467
Doug Zongker1807e702012-02-28 12:21:08 -0800468 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700469 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700470
Doug Zongker55d93282011-01-25 17:03:34 -0800471 boot_img = common.GetBootableImage("boot.img", "boot.img",
472 OPTIONS.input_tmp, "BOOT")
Doug Zongkerb32161a2012-08-21 10:33:44 -0700473 MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700474
Doug Zongker283e2a12010-03-15 17:52:32 -0700475 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700476 Item.Get("system").SetPermissions(script)
477
Doug Zongker37974732010-09-16 17:44:38 -0700478 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700479 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700480 script.ShowProgress(0.2, 0)
481
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700482 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700483 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700484
485 script.ShowProgress(0.1, 0)
486 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700487
Doug Zongker1c390a22009-05-14 19:06:36 -0700488 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700489 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700490
Doug Zongker14833602010-02-02 13:12:04 -0800491 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800492
493 if OPTIONS.two_step:
494 script.AppendExtra("""
495set_stage("%(bcb_dev)s", "");
496""" % bcb_dev)
497 script.AppendExtra("else\n")
498 script.WriteRawImage("/boot", "recovery.img")
499 script.AppendExtra("""
500set_stage("%(bcb_dev)s", "2/3");
501reboot_now("%(bcb_dev)s", "");
502endif;
503endif;
504""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700505 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700506 WriteMetadata(metadata, output_zip)
507
Stephen Smalley56882bf2012-02-09 13:36:21 -0500508def WritePolicyConfig(file_context, output_zip):
509 f = open(file_context, 'r');
510 basename = os.path.basename(file_context)
511 common.ZipWriteStr(output_zip, basename, f.read())
512
Doug Zongker2ea21062010-04-28 16:05:21 -0700513
514def WriteMetadata(metadata, output_zip):
515 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
516 "".join(["%s=%s\n" % kv
517 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700518
Doug Zongkereef39442009-04-02 12:14:19 -0700519def LoadSystemFiles(z):
520 """Load all the files from SYSTEM/... in a given target-files
521 ZipFile, and return a dict of {filename: File object}."""
522 out = {}
523 for info in z.infolist():
524 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700525 basefilename = info.filename[7:]
526 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700527 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700528 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800529 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700530
531
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700532def GetBuildProp(prop, info_dict):
533 """Return the fingerprint of the build of a given target-files info_dict."""
534 try:
535 return info_dict.get("build.prop", {})[prop]
536 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700537 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700538
539
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700540def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700541 source_version = OPTIONS.source_info_dict["recovery_api_version"]
542 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700543
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700544 if source_version == 0:
545 print ("WARNING: generating edify script for a source that "
546 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700547 script = edify_generator.EdifyGenerator(source_version,
548 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700549
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700550 metadata = {"pre-device": GetBuildProp("ro.product.device",
551 OPTIONS.source_info_dict),
552 "post-timestamp": GetBuildProp("ro.build.date.utc",
553 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700554 }
555
Doug Zongker05d3dea2009-06-22 11:32:31 -0700556 device_specific = common.DeviceSpecificParams(
557 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800558 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700559 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800560 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700561 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700562 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700563 metadata=metadata,
564 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700565
Doug Zongkereef39442009-04-02 12:14:19 -0700566 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800567 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700568 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800569 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700570
571 verbatim_targets = []
572 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700573 diffs = []
Doug Zongkereef39442009-04-02 12:14:19 -0700574 largest_source_size = 0
575 for fn in sorted(target_data.keys()):
576 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700577 assert fn == tf.name
Michael Runge90c60d32013-11-22 00:52:51 +0000578 sf = source_data.get(fn, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700579
580 if sf is None or fn in OPTIONS.require_verbatim:
581 # This file should be included verbatim
582 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700583 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700584 print "send", fn, "verbatim"
585 tf.AddToZip(output_zip)
586 verbatim_targets.append((fn, tf.size))
587 elif tf.sha1 != sf.sha1:
588 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700589 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700590 else:
Michael Runge90c60d32013-11-22 00:52:51 +0000591 # Target file identical to source.
Doug Zongkereef39442009-04-02 12:14:19 -0700592 pass
593
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700594 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700595
596 for diff in diffs:
597 tf, sf, d = diff.GetPatch()
598 if d is None or len(d) > tf.size * OPTIONS.patch_threshold:
599 # patch is almost as big as the file; don't bother patching
600 tf.AddToZip(output_zip)
601 verbatim_targets.append((tf.name, tf.size))
602 else:
Michael Runge90c60d32013-11-22 00:52:51 +0000603 common.ZipWriteStr(output_zip, "patch/" + tf.name + ".p", d)
604 patch_list.append((tf.name, tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700605 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700606
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700607 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
608 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700609 metadata["pre-build"] = source_fp
610 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700611
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700612 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700613 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700614
Doug Zongker55d93282011-01-25 17:03:34 -0800615 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700616 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
617 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800618 target_boot = common.GetBootableImage(
619 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800620 updating_boot = (not OPTIONS.two_step and
621 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700622
Doug Zongker55d93282011-01-25 17:03:34 -0800623 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700624 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
625 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800626 target_recovery = common.GetBootableImage(
627 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700628 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700629
Doug Zongker881dd402009-09-20 14:03:55 -0700630 # Here's how we divide up the progress bar:
631 # 0.1 for verifying the start state (PatchCheck calls)
632 # 0.8 for applying patches (ApplyPatch calls)
633 # 0.1 for unpacking verbatim files, symlinking, and doing the
634 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700635
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700636 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700637 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700638
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800639 # Two-step incremental package strategy (in chronological order,
640 # which is *not* the order in which the generated script has
641 # things):
642 #
643 # if stage is not "2/3" or "3/3":
644 # do verification on current system
645 # write recovery image to boot partition
646 # set stage to "2/3"
647 # reboot to boot partition and restart recovery
648 # else if stage is "2/3":
649 # write recovery image to recovery partition
650 # set stage to "3/3"
651 # reboot to recovery partition and restart recovery
652 # else:
653 # (stage must be "3/3")
654 # perform update:
655 # patch system files, etc.
656 # force full install of new boot image
657 # set up system to update recovery partition on first boot
658 # complete script normally (allow recovery to mark itself finished and reboot)
659
660 if OPTIONS.two_step:
661 if not OPTIONS.info_dict.get("multistage_support", None):
662 assert False, "two-step packages not supported by this build"
663 fs = OPTIONS.info_dict["fstab"]["/misc"]
664 assert fs.fs_type.upper() == "EMMC", \
665 "two-step packages only supported on devices with EMMC /misc partitions"
666 bcb_dev = {"bcb_dev": fs.device}
667 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
668 script.AppendExtra("""
669if get_stage("%(bcb_dev)s", "stage") == "2/3" then
670""" % bcb_dev)
671 script.AppendExtra("sleep(20);\n");
672 script.WriteRawImage("/recovery", "recovery.img")
673 script.AppendExtra("""
674set_stage("%(bcb_dev)s", "3/3");
675reboot_now("%(bcb_dev)s", "recovery");
676else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
677""" % bcb_dev)
678
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700679 script.Print("Verifying current system...")
680
Doug Zongkere5ff5902012-01-17 10:55:37 -0800681 device_specific.IncrementalOTA_VerifyBegin()
682
Doug Zongker881dd402009-09-20 14:03:55 -0700683 script.ShowProgress(0.1, 0)
684 total_verify_size = float(sum([i[2].size for i in patch_list]) + 1)
685 if updating_boot:
686 total_verify_size += source_boot.size
687 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700688
Doug Zongker5a482092010-02-17 16:09:18 -0800689 for fn, tf, sf, size, patch_sha in patch_list:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700690 script.PatchCheck("/"+fn, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700691 so_far += sf.size
692 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700693
Doug Zongker5da317e2009-06-02 13:38:17 -0700694 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700695 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700696 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700697 print "boot target: %d source: %d diff: %d" % (
698 target_boot.size, source_boot.size, len(d))
699
Doug Zongker048e7ca2009-06-15 14:31:53 -0700700 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700701
Doug Zongker96a57e72010-09-26 14:57:41 -0700702 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700703
704 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
705 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700706 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700707 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700708 so_far += source_boot.size
709 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700710
711 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700712 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800713
Doug Zongker05d3dea2009-06-22 11:32:31 -0700714 device_specific.IncrementalOTA_VerifyEnd()
715
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800716 if OPTIONS.two_step:
717 script.WriteRawImage("/boot", "recovery.img")
718 script.AppendExtra("""
719set_stage("%(bcb_dev)s", "2/3");
720reboot_now("%(bcb_dev)s", "");
721else
722""" % bcb_dev)
723
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700724 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700725
Doug Zongkere5ff5902012-01-17 10:55:37 -0800726 device_specific.IncrementalOTA_InstallBegin()
727
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800728 if OPTIONS.two_step:
729 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
730 script.WriteRawImage("/boot", "boot.img")
731 print "writing full boot image (forced by two-step mode)"
732
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700733 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700734 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700735 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700736
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700737 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700738 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
739 ["/"+i for i in sorted(source_data)
Michael Runge90c60d32013-11-22 00:52:51 +0000740 if i not in target_data] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700741 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700742
Doug Zongker881dd402009-09-20 14:03:55 -0700743 script.ShowProgress(0.8, 0)
744 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
745 if updating_boot:
746 total_patch_size += target_boot.size
747 so_far = 0
748
749 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700750 deferred_patch_list = []
751 for item in patch_list:
752 fn, tf, sf, size, _ = item
753 if tf.name == "system/build.prop":
754 deferred_patch_list.append(item)
755 continue
Doug Zongkerc8d446b2010-02-22 15:41:53 -0800756 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700757 so_far += tf.size
758 script.SetProgress(so_far / total_patch_size)
759
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800760 if not OPTIONS.two_step:
761 if updating_boot:
762 # Produce the boot image by applying a patch to the current
763 # contents of the boot partition, and write it back to the
764 # partition.
765 script.Print("Patching boot image...")
766 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
767 % (boot_type, boot_device,
768 source_boot.size, source_boot.sha1,
769 target_boot.size, target_boot.sha1),
770 "-",
771 target_boot.size, target_boot.sha1,
772 source_boot.sha1, "patch/boot.img.p")
773 so_far += target_boot.size
774 script.SetProgress(so_far / total_patch_size)
775 print "boot image changed; including."
776 else:
777 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -0700778
779 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700780 # Recovery is generated as a patch using both the boot image
781 # (which contains the same linux kernel as recovery) and the file
782 # /system/etc/recovery-resource.dat (which contains all the images
783 # used in the recovery UI) as sources. This lets us minimize the
784 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700785 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700786 # For older builds where recovery-resource.dat is not present, we
787 # use only the boot image as the source.
788
789 MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
790 target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800791 script.DeleteFiles(["/system/recovery-from-boot.p",
792 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700793 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700794 else:
795 print "recovery image unchanged; skipping."
796
Doug Zongker881dd402009-09-20 14:03:55 -0700797 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700798
Doug Zongker1807e702012-02-28 12:21:08 -0800799 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700800
801 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700802 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700803 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700804 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700805
806 # Note that this call will mess up the tree of Items, so make sure
807 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800808 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700809 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
810
811 # Delete all the symlinks in source that aren't in target. This
812 # needs to happen before verbatim files are unpacked, in case a
813 # symlink in the source is replaced by a real file in the target.
814 to_delete = []
815 for dest, link in source_symlinks:
816 if link not in target_symlinks_d:
817 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700818 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700819
820 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700821 script.Print("Unpacking new files...")
822 script.UnpackPackageDir("system", "/system")
823
Doug Zongker42265392010-02-12 10:21:00 -0800824 if updating_recovery:
825 script.Print("Unpacking new recovery...")
826 script.UnpackPackageDir("recovery", "/system")
827
Doug Zongker05d3dea2009-06-22 11:32:31 -0700828 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700829
830 # Create all the symlinks that don't already exist, or point to
831 # somewhere different than what we want. Delete each symlink before
832 # creating it, since the 'symlink' command won't overwrite.
833 to_create = []
834 for dest, link in target_symlinks:
835 if link in source_symlinks_d:
836 if dest != source_symlinks_d[link]:
837 to_create.append((dest, link))
838 else:
839 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700840 script.DeleteFiles([i[1] for i in to_create])
841 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700842
843 # Now that the symlinks are created, we can set all the
844 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700845 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700846
Doug Zongker881dd402009-09-20 14:03:55 -0700847 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700848 device_specific.IncrementalOTA_InstallEnd()
849
Doug Zongker1c390a22009-05-14 19:06:36 -0700850 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700851 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700852
Doug Zongkere92f15a2011-08-26 13:46:40 -0700853 # Patch the build.prop file last, so if something fails but the
854 # device can still come up, it appears to be the old build and will
855 # get set the OTA package again to retry.
856 script.Print("Patching remaining system files...")
857 for item in deferred_patch_list:
858 fn, tf, sf, size, _ = item
859 script.ApplyPatch("/"+fn, "-", tf.size, tf.sha1, sf.sha1, "patch/"+fn+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700860 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700861
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800862 if OPTIONS.two_step:
863 script.AppendExtra("""
864set_stage("%(bcb_dev)s", "");
865endif;
866endif;
867""" % bcb_dev)
868
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700869 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700870 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700871
872
873def main(argv):
874
875 def option_handler(o, a):
876 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700877 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700878 elif o in ("-k", "--package_key"):
879 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700880 elif o in ("-i", "--incremental_from"):
881 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700882 elif o in ("-w", "--wipe_user_data"):
883 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700884 elif o in ("-n", "--no_prereq"):
885 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700886 elif o in ("-e", "--extra_script"):
887 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700888 elif o in ("-a", "--aslr_mode"):
889 if a in ("on", "On", "true", "True", "yes", "Yes"):
890 OPTIONS.aslr_mode = True
891 else:
892 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700893 elif o in ("--worker_threads"):
894 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800895 elif o in ("-2", "--two_step"):
896 OPTIONS.two_step = True
Doug Zongkereef39442009-04-02 12:14:19 -0700897 else:
898 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700899 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700900
901 args = common.ParseOptions(argv, __doc__,
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800902 extra_opts="b:k:i:d:wne:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -0700903 extra_long_opts=["board_config=",
904 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700905 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700906 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700907 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700908 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700909 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700910 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800911 "two_step",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700912 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700913 extra_option_handler=option_handler)
914
915 if len(args) != 2:
916 common.Usage(__doc__)
917 sys.exit(1)
918
Doug Zongker1c390a22009-05-14 19:06:36 -0700919 if OPTIONS.extra_script is not None:
920 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
921
Doug Zongkereef39442009-04-02 12:14:19 -0700922 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800923 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700924
Doug Zongkereef39442009-04-02 12:14:19 -0700925 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -0700926 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -0700927
928 # If this image was originally labelled with SELinux contexts, make sure we
929 # also apply the labels in our new image. During building, the "file_contexts"
930 # is in the out/ directory tree, but for repacking from target-files.zip it's
931 # in the root directory of the ramdisk.
932 if "selinux_fc" in OPTIONS.info_dict:
933 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
934 "file_contexts")
935
Doug Zongker37974732010-09-16 17:44:38 -0700936 if OPTIONS.verbose:
937 print "--- target info ---"
938 common.DumpInfoDict(OPTIONS.info_dict)
939
940 if OPTIONS.device_specific is None:
941 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
942 if OPTIONS.device_specific is not None:
943 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
944 print "using device-specific extensions in", OPTIONS.device_specific
945
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700946 temp_zip_file = tempfile.NamedTemporaryFile()
947 output_zip = zipfile.ZipFile(temp_zip_file, "w",
948 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -0700949
950 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700951 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700952 if OPTIONS.package_key is None:
953 OPTIONS.package_key = OPTIONS.info_dict.get(
954 "default_system_dev_certificate",
955 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -0700956 else:
957 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800958 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -0700959 OPTIONS.target_info_dict = OPTIONS.info_dict
960 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700961 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -0700962 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700963 "default_system_dev_certificate",
964 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -0700965 if OPTIONS.verbose:
966 print "--- source info ---"
967 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700968 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700969
970 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700971
972 SignOutput(temp_zip_file.name, args[1])
973 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700974
975 common.Cleanup()
976
977 print "done."
978
979
980if __name__ == '__main__':
981 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -0800982 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -0700983 main(sys.argv[1:])
984 except common.ExternalError, e:
985 print
986 print " ERROR: %s" % (e,)
987 print
988 sys.exit(1)