blob: 11740751337d0150e04d51c0ddab03c8ec0d8135 [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
Takeshi Kanemotoe153b342013-11-14 17:20:50 +090097OPTIONS.no_signing = False
Doug Zongkereef39442009-04-02 12:14:19 -070098
99def MostPopularKey(d, default):
100 """Given a dict, return the key corresponding to the largest
101 value. Returns 'default' if the dict is empty."""
102 x = [(v, k) for (k, v) in d.iteritems()]
103 if not x: return default
104 x.sort()
105 return x[-1][1]
106
107
108def IsSymlink(info):
109 """Return true if the zipfile.ZipInfo object passed in represents a
110 symlink."""
111 return (info.external_attr >> 16) == 0120777
112
Hristo Bojinov96be7202010-08-02 10:26:17 -0700113def IsRegular(info):
114 """Return true if the zipfile.ZipInfo object passed in represents a
115 symlink."""
116 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700117
Michael Runge4038aa82013-12-13 18:06:28 -0800118def ClosestFileMatch(src, tgtfiles, existing):
119 """Returns the closest file match between a source file and list
120 of potential matches. The exact filename match is preferred,
121 then the sha1 is searched for, and finally a file with the same
122 basename is evaluated. Rename support in the updater-binary is
123 required for the latter checks to be used."""
124
125 result = tgtfiles.get("path:" + src.name)
126 if result is not None:
127 return result
128
129 if not OPTIONS.target_info_dict.get("update_rename_support", False):
130 return None
131
132 if src.size < 1000:
133 return None
134
135 result = tgtfiles.get("sha1:" + src.sha1)
136 if result is not None and existing.get(result.name) is None:
137 return result
138 result = tgtfiles.get("file:" + src.name.split("/")[-1])
139 if result is not None and existing.get(result.name) is None:
140 return result
141 return None
142
Doug Zongkereef39442009-04-02 12:14:19 -0700143class Item:
144 """Items represent the metadata (user, group, mode) of files and
145 directories in the system image."""
146 ITEMS = {}
147 def __init__(self, name, dir=False):
148 self.name = name
149 self.uid = None
150 self.gid = None
151 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700152 self.selabel = None
153 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700154 self.dir = dir
155
156 if name:
157 self.parent = Item.Get(os.path.dirname(name), dir=True)
158 self.parent.children.append(self)
159 else:
160 self.parent = None
161 if dir:
162 self.children = []
163
164 def Dump(self, indent=0):
165 if self.uid is not None:
166 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
167 else:
168 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
169 if self.dir:
170 print "%s%s" % (" "*indent, self.descendants)
171 print "%s%s" % (" "*indent, self.best_subtree)
172 for i in self.children:
173 i.Dump(indent=indent+1)
174
175 @classmethod
176 def Get(cls, name, dir=False):
177 if name not in cls.ITEMS:
178 cls.ITEMS[name] = Item(name, dir=dir)
179 return cls.ITEMS[name]
180
181 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700182 def GetMetadata(cls, input_zip):
183
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700184 # The target_files contains a record of what the uid,
185 # gid, and mode are supposed to be.
186 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700187
188 for line in output.split("\n"):
189 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700190 columns = line.split()
191 name, uid, gid, mode = columns[:4]
192 selabel = None
193 capabilities = None
194
195 # After the first 4 columns, there are a series of key=value
196 # pairs. Extract out the fields we care about.
197 for element in columns[4:]:
198 key, value = element.split("=")
199 if key == "selabel":
200 selabel = value
201 if key == "capabilities":
202 capabilities = value
203
Doug Zongker283e2a12010-03-15 17:52:32 -0700204 i = cls.ITEMS.get(name, None)
205 if i is not None:
206 i.uid = int(uid)
207 i.gid = int(gid)
208 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700209 i.selabel = selabel
210 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700211 if i.dir:
212 i.children.sort(key=lambda i: i.name)
213
214 # set metadata for the files generated by this script.
215 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700216 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700217 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700218 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700219
220 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700221 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
222 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700223 set_perm to correctly chown/chmod all the files to their desired
224 values. Recursively calls itself for all descendants.
225
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700226 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700227 all descendants of this node. (dmode or fmode may be None.) Also
228 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700229 dmode, fmode, selabel, capabilities) tuple that will match the most
230 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700231 """
232
233 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700234 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700235 for i in self.children:
236 if i.dir:
237 for k, v in i.CountChildMetadata().iteritems():
238 d[k] = d.get(k, 0) + v
239 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700240 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700241 d[k] = d.get(k, 0) + 1
242
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700243 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
244 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700245
246 # First, find the (uid, gid) pair that matches the most
247 # descendants.
248 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700249 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700250 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
251 ug = MostPopularKey(ug, (0, 0))
252
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700253 # Now find the dmode, fmode, selabel, and capabilities that match
254 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700255 best_dmode = (0, 0755)
256 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 best_selabel = (0, None)
258 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700259 for k, count in d.iteritems():
260 if k[:2] != ug: continue
261 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
262 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700263 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
264 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
265 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700266
267 return d
268
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700269 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700270 """Append set_perm/set_perm_recursive commands to 'script' to
271 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700272 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700273
274 self.CountChildMetadata()
275
276 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700277 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700278 # item (and all its children) have already been set to. We only
279 # need to issue set_perm/set_perm_recursive commands if we're
280 # supposed to be something different.
281 if item.dir:
282 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700283 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700284 current = item.best_subtree
285
286 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700287 item.mode != current[2] or item.selabel != current[4] or \
288 item.capabilities != current[5]:
289 script.SetPermissions("/"+item.name, item.uid, item.gid,
290 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700291
292 for i in item.children:
293 recurse(i, current)
294 else:
295 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700296 item.mode != current[3] or item.selabel != current[4] or \
297 item.capabilities != current[5]:
298 script.SetPermissions("/"+item.name, item.uid, item.gid,
299 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700300
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700301 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700302
303
304def CopySystemFiles(input_zip, output_zip=None,
305 substitute=None):
306 """Copies files underneath system/ in the input zip to the output
307 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800308 list of symlinks. output_zip may be None, in which case the copy is
309 skipped (but the other side effects still happen). substitute is an
310 optional dict of {output filename: contents} to be output instead of
311 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700312 """
313
314 symlinks = []
315
316 for info in input_zip.infolist():
317 if info.filename.startswith("SYSTEM/"):
318 basefilename = info.filename[7:]
319 if IsSymlink(info):
320 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700321 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700322 else:
323 info2 = copy.copy(info)
324 fn = info2.filename = "system/" + basefilename
325 if substitute and fn in substitute and substitute[fn] is None:
326 continue
327 if output_zip is not None:
328 if substitute and fn in substitute:
329 data = substitute[fn]
330 else:
331 data = input_zip.read(info.filename)
332 output_zip.writestr(info2, data)
333 if fn.endswith("/"):
334 Item.Get(fn[:-1], dir=True)
335 else:
336 Item.Get(fn, dir=False)
337
338 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800339 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700340
341
Doug Zongkereef39442009-04-02 12:14:19 -0700342def SignOutput(temp_zip_name, output_zip_name):
343 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
344 pw = key_passwords[OPTIONS.package_key]
345
Doug Zongker951495f2009-08-14 12:44:19 -0700346 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
347 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700348
349
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700350def AppendAssertions(script, info_dict):
351 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700352 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700353
Doug Zongkereef39442009-04-02 12:14:19 -0700354
Doug Zongkerb32161a2012-08-21 10:33:44 -0700355def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700356 """Generate a binary patch that creates the recovery image starting
357 with the boot image. (Most of the space in these images is just the
358 kernel, which is identical for the two, so the resulting patch
359 should be efficient.) Add it to the output zip, along with a shell
360 script that is run from init.rc on first boot to actually do the
361 patching and install the new recovery image.
362
363 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700364 corresponding images. info should be the dictionary returned by
365 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700366
367 Returns an Item for the shell script, which must be made
368 executable.
369 """
370
Doug Zongkerb32161a2012-08-21 10:33:44 -0700371 diff_program = ["imgdiff"]
372 path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
373 if os.path.exists(path):
374 diff_program.append("-b")
375 diff_program.append(path)
376 bonus_args = "-b /system/etc/recovery-resource.dat"
377 else:
378 bonus_args = ""
379
380 d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
Doug Zongker761e6422009-09-25 10:45:39 -0700381 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700382 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700383 Item.Get("system/recovery-from-boot.p", dir=False)
384
Doug Zongker96a57e72010-09-26 14:57:41 -0700385 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
386 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700387
Doug Zongker73ef8252009-07-23 15:12:53 -0700388 sh = """#!/system/bin/sh
Doug Zongker0276d182011-12-02 10:46:59 -0800389if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700390 log -t recovery "Installing new recovery image"
Doug Zongkerb32161a2012-08-21 10:33:44 -0700391 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 -0700392else
393 log -t recovery "Recovery image already installed"
394fi
395""" % { 'boot_size': boot_img.size,
396 'boot_sha1': boot_img.sha1,
Doug Zongker73ef8252009-07-23 15:12:53 -0700397 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700398 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700399 'boot_type': boot_type,
400 'boot_device': boot_device,
401 'recovery_type': recovery_type,
402 'recovery_device': recovery_device,
Doug Zongkerb32161a2012-08-21 10:33:44 -0700403 'bonus_args': bonus_args,
Doug Zongker67369982010-07-07 13:53:32 -0700404 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700405 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700406 return Item.Get("system/etc/install-recovery.sh", dir=False)
407
408
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700409def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700410 # TODO: how to determine this? We don't know what version it will
411 # be installed on top of. For now, we expect the API just won't
412 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700413 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700414
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700415 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
416 OPTIONS.info_dict),
417 "pre-device": GetBuildProp("ro.product.device",
418 OPTIONS.info_dict),
419 "post-timestamp": GetBuildProp("ro.build.date.utc",
420 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700421 }
422
Doug Zongker05d3dea2009-06-22 11:32:31 -0700423 device_specific = common.DeviceSpecificParams(
424 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700425 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700426 output_zip=output_zip,
427 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700428 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700429 metadata=metadata,
430 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700431
Doug Zongker962069c2009-04-23 11:41:58 -0700432 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700433 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700434 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
435 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700436
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700437 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700438 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800439
440 # Two-step package strategy (in chronological order, which is *not*
441 # the order in which the generated script has things):
442 #
443 # if stage is not "2/3" or "3/3":
444 # write recovery image to boot partition
445 # set stage to "2/3"
446 # reboot to boot partition and restart recovery
447 # else if stage is "2/3":
448 # write recovery image to recovery partition
449 # set stage to "3/3"
450 # reboot to recovery partition and restart recovery
451 # else:
452 # (stage must be "3/3")
453 # set stage to ""
454 # do normal full package installation:
455 # wipe and install system, boot image, etc.
456 # set up system to update recovery partition on first boot
457 # complete script normally (allow recovery to mark itself finished and reboot)
458
459 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
460 OPTIONS.input_tmp, "RECOVERY")
461 if OPTIONS.two_step:
462 if not OPTIONS.info_dict.get("multistage_support", None):
463 assert False, "two-step packages not supported by this build"
464 fs = OPTIONS.info_dict["fstab"]["/misc"]
465 assert fs.fs_type.upper() == "EMMC", \
466 "two-step packages only supported on devices with EMMC /misc partitions"
467 bcb_dev = {"bcb_dev": fs.device}
468 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
469 script.AppendExtra("""
470if get_stage("%(bcb_dev)s", "stage") == "2/3" then
471""" % bcb_dev)
472 script.WriteRawImage("/recovery", "recovery.img")
473 script.AppendExtra("""
474set_stage("%(bcb_dev)s", "3/3");
475reboot_now("%(bcb_dev)s", "recovery");
476else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
477""" % bcb_dev)
478
Doug Zongkere5ff5902012-01-17 10:55:37 -0800479 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700480
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700481 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700482
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700483 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700484 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700485
Kenny Rootf32dc712012-04-08 10:42:34 -0700486 if "selinux_fc" in OPTIONS.info_dict:
487 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500488
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700489 script.FormatPartition("/system")
490 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700491 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700492 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700493
Doug Zongker1807e702012-02-28 12:21:08 -0800494 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700495 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700496
Doug Zongker55d93282011-01-25 17:03:34 -0800497 boot_img = common.GetBootableImage("boot.img", "boot.img",
498 OPTIONS.input_tmp, "BOOT")
Doug Zongkerb32161a2012-08-21 10:33:44 -0700499 MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700500
Doug Zongker283e2a12010-03-15 17:52:32 -0700501 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700502 Item.Get("system").SetPermissions(script)
503
Doug Zongker37974732010-09-16 17:44:38 -0700504 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700505 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700506 script.ShowProgress(0.2, 0)
507
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700508 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700509 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700510
511 script.ShowProgress(0.1, 0)
512 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700513
Doug Zongker1c390a22009-05-14 19:06:36 -0700514 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700515 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700516
Doug Zongker14833602010-02-02 13:12:04 -0800517 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800518
519 if OPTIONS.two_step:
520 script.AppendExtra("""
521set_stage("%(bcb_dev)s", "");
522""" % bcb_dev)
523 script.AppendExtra("else\n")
524 script.WriteRawImage("/boot", "recovery.img")
525 script.AppendExtra("""
526set_stage("%(bcb_dev)s", "2/3");
527reboot_now("%(bcb_dev)s", "");
528endif;
529endif;
530""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700531 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700532 WriteMetadata(metadata, output_zip)
533
Stephen Smalley56882bf2012-02-09 13:36:21 -0500534def WritePolicyConfig(file_context, output_zip):
535 f = open(file_context, 'r');
536 basename = os.path.basename(file_context)
537 common.ZipWriteStr(output_zip, basename, f.read())
538
Doug Zongker2ea21062010-04-28 16:05:21 -0700539
540def WriteMetadata(metadata, output_zip):
541 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
542 "".join(["%s=%s\n" % kv
543 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700544
Doug Zongkereef39442009-04-02 12:14:19 -0700545def LoadSystemFiles(z):
546 """Load all the files from SYSTEM/... in a given target-files
547 ZipFile, and return a dict of {filename: File object}."""
548 out = {}
549 for info in z.infolist():
550 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700551 basefilename = info.filename[7:]
552 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700553 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700554 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800555 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700556
557
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700558def GetBuildProp(prop, info_dict):
559 """Return the fingerprint of the build of a given target-files info_dict."""
560 try:
561 return info_dict.get("build.prop", {})[prop]
562 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700563 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700564
Michael Runge4038aa82013-12-13 18:06:28 -0800565def AddToKnownPaths(filename, known_paths):
566 if filename[-1] == "/":
567 return
568 dirs = filename.split("/")[:-1]
569 while len(dirs) > 0:
570 path = "/".join(dirs)
571 if path in known_paths:
572 break;
573 known_paths.add(path)
574 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700575
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700576def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700577 source_version = OPTIONS.source_info_dict["recovery_api_version"]
578 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700579
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700580 if source_version == 0:
581 print ("WARNING: generating edify script for a source that "
582 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700583 script = edify_generator.EdifyGenerator(source_version,
584 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700585
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700586 metadata = {"pre-device": GetBuildProp("ro.product.device",
587 OPTIONS.source_info_dict),
588 "post-timestamp": GetBuildProp("ro.build.date.utc",
589 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700590 }
591
Doug Zongker05d3dea2009-06-22 11:32:31 -0700592 device_specific = common.DeviceSpecificParams(
593 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800594 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700595 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800596 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700597 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700598 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700599 metadata=metadata,
600 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700601
Doug Zongkereef39442009-04-02 12:14:19 -0700602 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800603 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700604 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800605 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700606
607 verbatim_targets = []
608 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700609 diffs = []
Michael Runge4038aa82013-12-13 18:06:28 -0800610 renames = {}
611 known_paths = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700612 largest_source_size = 0
Michael Runge4038aa82013-12-13 18:06:28 -0800613
614 matching_file_cache = {}
615 for fn, sf in source_data.items():
616 assert fn == sf.name
617 matching_file_cache["path:" + fn] = sf
618 if fn in target_data.keys():
619 AddToKnownPaths(fn, known_paths)
620 # Only allow eligibility for filename/sha matching
621 # if there isn't a perfect path match.
622 if target_data.get(sf.name) is None:
623 matching_file_cache["file:" + fn.split("/")[-1]] = sf
624 matching_file_cache["sha:" + sf.sha1] = sf
625
Doug Zongkereef39442009-04-02 12:14:19 -0700626 for fn in sorted(target_data.keys()):
627 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700628 assert fn == tf.name
Michael Runge4038aa82013-12-13 18:06:28 -0800629 sf = ClosestFileMatch(tf, matching_file_cache, renames)
630 if sf is not None and sf.name != tf.name:
631 print "File has moved from " + sf.name + " to " + tf.name
632 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700633
634 if sf is None or fn in OPTIONS.require_verbatim:
635 # This file should be included verbatim
636 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700637 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700638 print "send", fn, "verbatim"
639 tf.AddToZip(output_zip)
640 verbatim_targets.append((fn, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800641 if fn in target_data.keys():
642 AddToKnownPaths(fn, known_paths)
Doug Zongkereef39442009-04-02 12:14:19 -0700643 elif tf.sha1 != sf.sha1:
644 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700645 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700646 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800647 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700648 pass
649
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700650 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700651
652 for diff in diffs:
653 tf, sf, d = diff.GetPatch()
Michael Runge4038aa82013-12-13 18:06:28 -0800654 path = "/".join(tf.name.split("/")[:-1])
655 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
656 path not in known_paths:
Doug Zongker761e6422009-09-25 10:45:39 -0700657 # patch is almost as big as the file; don't bother patching
Michael Runge4038aa82013-12-13 18:06:28 -0800658 # or a patch + rename cannot take place due to the target
659 # directory not existing
Doug Zongker761e6422009-09-25 10:45:39 -0700660 tf.AddToZip(output_zip)
661 verbatim_targets.append((tf.name, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800662 if sf.name in renames:
663 del renames[sf.name]
664 AddToKnownPaths(tf.name, known_paths)
Doug Zongker761e6422009-09-25 10:45:39 -0700665 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800666 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
667 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700668 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700669
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700670 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
671 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700672 metadata["pre-build"] = source_fp
673 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700674
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700675 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700676 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700677
Doug Zongker55d93282011-01-25 17:03:34 -0800678 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700679 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
680 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800681 target_boot = common.GetBootableImage(
682 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800683 updating_boot = (not OPTIONS.two_step and
684 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700685
Doug Zongker55d93282011-01-25 17:03:34 -0800686 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700687 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
688 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800689 target_recovery = common.GetBootableImage(
690 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700691 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700692
Doug Zongker881dd402009-09-20 14:03:55 -0700693 # Here's how we divide up the progress bar:
694 # 0.1 for verifying the start state (PatchCheck calls)
695 # 0.8 for applying patches (ApplyPatch calls)
696 # 0.1 for unpacking verbatim files, symlinking, and doing the
697 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700698
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700699 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700700 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700701
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800702 # Two-step incremental package strategy (in chronological order,
703 # which is *not* the order in which the generated script has
704 # things):
705 #
706 # if stage is not "2/3" or "3/3":
707 # do verification on current system
708 # write recovery image to boot partition
709 # set stage to "2/3"
710 # reboot to boot partition and restart recovery
711 # else if stage is "2/3":
712 # write recovery image to recovery partition
713 # set stage to "3/3"
714 # reboot to recovery partition and restart recovery
715 # else:
716 # (stage must be "3/3")
717 # perform update:
718 # patch system files, etc.
719 # force full install of new boot image
720 # set up system to update recovery partition on first boot
721 # complete script normally (allow recovery to mark itself finished and reboot)
722
723 if OPTIONS.two_step:
724 if not OPTIONS.info_dict.get("multistage_support", None):
725 assert False, "two-step packages not supported by this build"
726 fs = OPTIONS.info_dict["fstab"]["/misc"]
727 assert fs.fs_type.upper() == "EMMC", \
728 "two-step packages only supported on devices with EMMC /misc partitions"
729 bcb_dev = {"bcb_dev": fs.device}
730 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
731 script.AppendExtra("""
732if get_stage("%(bcb_dev)s", "stage") == "2/3" then
733""" % bcb_dev)
734 script.AppendExtra("sleep(20);\n");
735 script.WriteRawImage("/recovery", "recovery.img")
736 script.AppendExtra("""
737set_stage("%(bcb_dev)s", "3/3");
738reboot_now("%(bcb_dev)s", "recovery");
739else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
740""" % bcb_dev)
741
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700742 script.Print("Verifying current system...")
743
Doug Zongkere5ff5902012-01-17 10:55:37 -0800744 device_specific.IncrementalOTA_VerifyBegin()
745
Doug Zongker881dd402009-09-20 14:03:55 -0700746 script.ShowProgress(0.1, 0)
Michael Runge4038aa82013-12-13 18:06:28 -0800747 total_verify_size = float(sum([i[1].size for i in patch_list]) + 1)
Doug Zongker881dd402009-09-20 14:03:55 -0700748 if updating_boot:
749 total_verify_size += source_boot.size
750 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700751
Michael Runge4038aa82013-12-13 18:06:28 -0800752 for tf, sf, size, patch_sha in patch_list:
753 if tf.name != sf.name:
754 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
755 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700756 so_far += sf.size
757 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700758
Doug Zongker5da317e2009-06-02 13:38:17 -0700759 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700760 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700761 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700762 print "boot target: %d source: %d diff: %d" % (
763 target_boot.size, source_boot.size, len(d))
764
Doug Zongker048e7ca2009-06-15 14:31:53 -0700765 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700766
Doug Zongker96a57e72010-09-26 14:57:41 -0700767 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700768
769 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
770 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700771 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700772 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700773 so_far += source_boot.size
774 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700775
776 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700777 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800778
Doug Zongker05d3dea2009-06-22 11:32:31 -0700779 device_specific.IncrementalOTA_VerifyEnd()
780
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800781 if OPTIONS.two_step:
782 script.WriteRawImage("/boot", "recovery.img")
783 script.AppendExtra("""
784set_stage("%(bcb_dev)s", "2/3");
785reboot_now("%(bcb_dev)s", "");
786else
787""" % bcb_dev)
788
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700789 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700790
Doug Zongkere5ff5902012-01-17 10:55:37 -0800791 device_specific.IncrementalOTA_InstallBegin()
792
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800793 if OPTIONS.two_step:
794 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
795 script.WriteRawImage("/boot", "boot.img")
796 print "writing full boot image (forced by two-step mode)"
797
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700798 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700799 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700800 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700801
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700802 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700803 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
804 ["/"+i for i in sorted(source_data)
Michael Runge4038aa82013-12-13 18:06:28 -0800805 if i not in target_data and
806 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700807 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700808
Doug Zongker881dd402009-09-20 14:03:55 -0700809 script.ShowProgress(0.8, 0)
810 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
811 if updating_boot:
812 total_patch_size += target_boot.size
813 so_far = 0
814
815 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700816 deferred_patch_list = []
817 for item in patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800818 tf, sf, size, _ = item
Doug Zongkere92f15a2011-08-26 13:46:40 -0700819 if tf.name == "system/build.prop":
820 deferred_patch_list.append(item)
821 continue
Michael Runge4038aa82013-12-13 18:06:28 -0800822 if (sf.name != tf.name):
823 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
824 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700825 so_far += tf.size
826 script.SetProgress(so_far / total_patch_size)
827
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800828 if not OPTIONS.two_step:
829 if updating_boot:
830 # Produce the boot image by applying a patch to the current
831 # contents of the boot partition, and write it back to the
832 # partition.
833 script.Print("Patching boot image...")
834 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
835 % (boot_type, boot_device,
836 source_boot.size, source_boot.sha1,
837 target_boot.size, target_boot.sha1),
838 "-",
839 target_boot.size, target_boot.sha1,
840 source_boot.sha1, "patch/boot.img.p")
841 so_far += target_boot.size
842 script.SetProgress(so_far / total_patch_size)
843 print "boot image changed; including."
844 else:
845 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -0700846
847 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700848 # Recovery is generated as a patch using both the boot image
849 # (which contains the same linux kernel as recovery) and the file
850 # /system/etc/recovery-resource.dat (which contains all the images
851 # used in the recovery UI) as sources. This lets us minimize the
852 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700853 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700854 # For older builds where recovery-resource.dat is not present, we
855 # use only the boot image as the source.
856
857 MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
858 target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800859 script.DeleteFiles(["/system/recovery-from-boot.p",
860 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700861 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700862 else:
863 print "recovery image unchanged; skipping."
864
Doug Zongker881dd402009-09-20 14:03:55 -0700865 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700866
Doug Zongker1807e702012-02-28 12:21:08 -0800867 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700868
869 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700870 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700871 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700872 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700873
874 # Note that this call will mess up the tree of Items, so make sure
875 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800876 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700877 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
878
879 # Delete all the symlinks in source that aren't in target. This
880 # needs to happen before verbatim files are unpacked, in case a
881 # symlink in the source is replaced by a real file in the target.
882 to_delete = []
883 for dest, link in source_symlinks:
884 if link not in target_symlinks_d:
885 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700886 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700887
888 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700889 script.Print("Unpacking new files...")
890 script.UnpackPackageDir("system", "/system")
891
Doug Zongker42265392010-02-12 10:21:00 -0800892 if updating_recovery:
893 script.Print("Unpacking new recovery...")
894 script.UnpackPackageDir("recovery", "/system")
895
Michael Runge4038aa82013-12-13 18:06:28 -0800896 if len(renames) > 0:
897 script.Print("Renaming files...")
898
899 for src in renames:
900 print "Renaming " + src + " to " + renames[src].name
901 script.RenameFile(src, renames[src].name)
902
Doug Zongker05d3dea2009-06-22 11:32:31 -0700903 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700904
905 # Create all the symlinks that don't already exist, or point to
906 # somewhere different than what we want. Delete each symlink before
907 # creating it, since the 'symlink' command won't overwrite.
908 to_create = []
909 for dest, link in target_symlinks:
910 if link in source_symlinks_d:
911 if dest != source_symlinks_d[link]:
912 to_create.append((dest, link))
913 else:
914 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700915 script.DeleteFiles([i[1] for i in to_create])
916 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700917
918 # Now that the symlinks are created, we can set all the
919 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700920 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700921
Doug Zongker881dd402009-09-20 14:03:55 -0700922 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700923 device_specific.IncrementalOTA_InstallEnd()
924
Doug Zongker1c390a22009-05-14 19:06:36 -0700925 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700926 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700927
Doug Zongkere92f15a2011-08-26 13:46:40 -0700928 # Patch the build.prop file last, so if something fails but the
929 # device can still come up, it appears to be the old build and will
930 # get set the OTA package again to retry.
931 script.Print("Patching remaining system files...")
932 for item in deferred_patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800933 tf, sf, size, _ = item
934 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700935 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700936
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800937 if OPTIONS.two_step:
938 script.AppendExtra("""
939set_stage("%(bcb_dev)s", "");
940endif;
941endif;
942""" % bcb_dev)
943
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700944 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700945 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700946
947
948def main(argv):
949
950 def option_handler(o, a):
951 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700952 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700953 elif o in ("-k", "--package_key"):
954 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700955 elif o in ("-i", "--incremental_from"):
956 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700957 elif o in ("-w", "--wipe_user_data"):
958 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700959 elif o in ("-n", "--no_prereq"):
960 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700961 elif o in ("-e", "--extra_script"):
962 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700963 elif o in ("-a", "--aslr_mode"):
964 if a in ("on", "On", "true", "True", "yes", "Yes"):
965 OPTIONS.aslr_mode = True
966 else:
967 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700968 elif o in ("--worker_threads"):
969 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800970 elif o in ("-2", "--two_step"):
971 OPTIONS.two_step = True
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900972 elif o in ("--no_signing"):
973 OPTIONS.no_signing = True
Doug Zongkereef39442009-04-02 12:14:19 -0700974 else:
975 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700976 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700977
978 args = common.ParseOptions(argv, __doc__,
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800979 extra_opts="b:k:i:d:wne:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -0700980 extra_long_opts=["board_config=",
981 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700982 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700983 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700984 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700985 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700986 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700987 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800988 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900989 "no_signing",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700990 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700991 extra_option_handler=option_handler)
992
993 if len(args) != 2:
994 common.Usage(__doc__)
995 sys.exit(1)
996
Doug Zongker1c390a22009-05-14 19:06:36 -0700997 if OPTIONS.extra_script is not None:
998 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
999
Doug Zongkereef39442009-04-02 12:14:19 -07001000 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001001 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001002
Doug Zongkereef39442009-04-02 12:14:19 -07001003 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001004 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001005
1006 # If this image was originally labelled with SELinux contexts, make sure we
1007 # also apply the labels in our new image. During building, the "file_contexts"
1008 # is in the out/ directory tree, but for repacking from target-files.zip it's
1009 # in the root directory of the ramdisk.
1010 if "selinux_fc" in OPTIONS.info_dict:
1011 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1012 "file_contexts")
1013
Doug Zongker37974732010-09-16 17:44:38 -07001014 if OPTIONS.verbose:
1015 print "--- target info ---"
1016 common.DumpInfoDict(OPTIONS.info_dict)
1017
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001018 # If the caller explicitly specified the device-specific extensions
1019 # path via -s/--device_specific, use that. Otherwise, use
1020 # META/releasetools.py if it is present in the target target_files.
1021 # Otherwise, take the path of the file from 'tool_extensions' in the
1022 # info dict and look for that in the local filesystem, relative to
1023 # the current directory.
1024
Doug Zongker37974732010-09-16 17:44:38 -07001025 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001026 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1027 if os.path.exists(from_input):
1028 print "(using device-specific extensions from target_files)"
1029 OPTIONS.device_specific = from_input
1030 else:
1031 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1032
Doug Zongker37974732010-09-16 17:44:38 -07001033 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001034 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001035
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001036 if OPTIONS.no_signing:
1037 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1038 else:
1039 temp_zip_file = tempfile.NamedTemporaryFile()
1040 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1041 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001042
1043 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001044 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001045 if OPTIONS.package_key is None:
1046 OPTIONS.package_key = OPTIONS.info_dict.get(
1047 "default_system_dev_certificate",
1048 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001049 else:
1050 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001051 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001052 OPTIONS.target_info_dict = OPTIONS.info_dict
1053 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001054 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001055 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001056 "default_system_dev_certificate",
1057 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001058 if OPTIONS.verbose:
1059 print "--- source info ---"
1060 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001061 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001062
1063 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001064
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001065 if not OPTIONS.no_signing:
1066 SignOutput(temp_zip_file.name, args[1])
1067 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001068
1069 common.Cleanup()
1070
1071 print "done."
1072
1073
1074if __name__ == '__main__':
1075 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001076 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001077 main(sys.argv[1:])
1078 except common.ExternalError, e:
1079 print
1080 print " ERROR: %s" % (e,)
1081 print
1082 sys.exit(1)