blob: e6952182262d06ed561e098a6b8cf8b780ec9f9f [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
Martin Blumenstingl374e1142014-05-31 20:42:55 +020060 -t (--worker_threads) <int>
61 Specifies the number of worker-threads that will be used when
62 generating patches for incremental updates (defaults to 3).
63
Doug Zongkereef39442009-04-02 12:14:19 -070064"""
65
66import sys
67
68if sys.hexversion < 0x02040000:
69 print >> sys.stderr, "Python 2.4 or newer is required."
70 sys.exit(1)
71
72import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070073import errno
Doug Zongkereef39442009-04-02 12:14:19 -070074import os
75import re
Doug Zongkereef39442009-04-02 12:14:19 -070076import subprocess
77import tempfile
78import time
79import zipfile
80
davidcad0bb92011-03-15 14:21:38 +000081try:
82 from hashlib import sha1 as sha1
83except ImportError:
84 from sha import sha as sha1
85
Doug Zongkereef39442009-04-02 12:14:19 -070086import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070087import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070088
89OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -070090OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -070091OPTIONS.incremental_source = None
92OPTIONS.require_verbatim = set()
93OPTIONS.prohibit_verbatim = set(("system/build.prop",))
94OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070095OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070096OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070097OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070098OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070099OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800100OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900101OPTIONS.no_signing = False
Doug Zongkereef39442009-04-02 12:14:19 -0700102
103def MostPopularKey(d, default):
104 """Given a dict, return the key corresponding to the largest
105 value. Returns 'default' if the dict is empty."""
106 x = [(v, k) for (k, v) in d.iteritems()]
107 if not x: return default
108 x.sort()
109 return x[-1][1]
110
111
112def IsSymlink(info):
113 """Return true if the zipfile.ZipInfo object passed in represents a
114 symlink."""
115 return (info.external_attr >> 16) == 0120777
116
Hristo Bojinov96be7202010-08-02 10:26:17 -0700117def IsRegular(info):
118 """Return true if the zipfile.ZipInfo object passed in represents a
119 symlink."""
120 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700121
Michael Runge4038aa82013-12-13 18:06:28 -0800122def ClosestFileMatch(src, tgtfiles, existing):
123 """Returns the closest file match between a source file and list
124 of potential matches. The exact filename match is preferred,
125 then the sha1 is searched for, and finally a file with the same
126 basename is evaluated. Rename support in the updater-binary is
127 required for the latter checks to be used."""
128
129 result = tgtfiles.get("path:" + src.name)
130 if result is not None:
131 return result
132
133 if not OPTIONS.target_info_dict.get("update_rename_support", False):
134 return None
135
136 if src.size < 1000:
137 return None
138
139 result = tgtfiles.get("sha1:" + src.sha1)
140 if result is not None and existing.get(result.name) is None:
141 return result
142 result = tgtfiles.get("file:" + src.name.split("/")[-1])
143 if result is not None and existing.get(result.name) is None:
144 return result
145 return None
146
Doug Zongkereef39442009-04-02 12:14:19 -0700147class Item:
148 """Items represent the metadata (user, group, mode) of files and
149 directories in the system image."""
150 ITEMS = {}
151 def __init__(self, name, dir=False):
152 self.name = name
153 self.uid = None
154 self.gid = None
155 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700156 self.selabel = None
157 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700158 self.dir = dir
159
160 if name:
161 self.parent = Item.Get(os.path.dirname(name), dir=True)
162 self.parent.children.append(self)
163 else:
164 self.parent = None
165 if dir:
166 self.children = []
167
168 def Dump(self, indent=0):
169 if self.uid is not None:
170 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
171 else:
172 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
173 if self.dir:
174 print "%s%s" % (" "*indent, self.descendants)
175 print "%s%s" % (" "*indent, self.best_subtree)
176 for i in self.children:
177 i.Dump(indent=indent+1)
178
179 @classmethod
180 def Get(cls, name, dir=False):
181 if name not in cls.ITEMS:
182 cls.ITEMS[name] = Item(name, dir=dir)
183 return cls.ITEMS[name]
184
185 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700186 def GetMetadata(cls, input_zip):
187
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700188 # The target_files contains a record of what the uid,
189 # gid, and mode are supposed to be.
190 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700191
192 for line in output.split("\n"):
193 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700194 columns = line.split()
195 name, uid, gid, mode = columns[:4]
196 selabel = None
197 capabilities = None
198
199 # After the first 4 columns, there are a series of key=value
200 # pairs. Extract out the fields we care about.
201 for element in columns[4:]:
202 key, value = element.split("=")
203 if key == "selabel":
204 selabel = value
205 if key == "capabilities":
206 capabilities = value
207
Doug Zongker283e2a12010-03-15 17:52:32 -0700208 i = cls.ITEMS.get(name, None)
209 if i is not None:
210 i.uid = int(uid)
211 i.gid = int(gid)
212 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700213 i.selabel = selabel
214 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700215 if i.dir:
216 i.children.sort(key=lambda i: i.name)
217
218 # set metadata for the files generated by this script.
219 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700220 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700221 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700222 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700223
224 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700225 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
226 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700227 set_perm to correctly chown/chmod all the files to their desired
228 values. Recursively calls itself for all descendants.
229
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700230 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700231 all descendants of this node. (dmode or fmode may be None.) Also
232 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700233 dmode, fmode, selabel, capabilities) tuple that will match the most
234 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700235 """
236
237 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700238 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700239 for i in self.children:
240 if i.dir:
241 for k, v in i.CountChildMetadata().iteritems():
242 d[k] = d.get(k, 0) + v
243 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700244 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700245 d[k] = d.get(k, 0) + 1
246
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700247 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
248 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700249
250 # First, find the (uid, gid) pair that matches the most
251 # descendants.
252 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700253 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700254 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
255 ug = MostPopularKey(ug, (0, 0))
256
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 # Now find the dmode, fmode, selabel, and capabilities that match
258 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700259 best_dmode = (0, 0755)
260 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700261 best_selabel = (0, None)
262 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700263 for k, count in d.iteritems():
264 if k[:2] != ug: continue
265 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
266 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700267 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
268 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
269 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700270
271 return d
272
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700273 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700274 """Append set_perm/set_perm_recursive commands to 'script' to
275 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700276 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700277
278 self.CountChildMetadata()
279
280 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700281 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700282 # item (and all its children) have already been set to. We only
283 # need to issue set_perm/set_perm_recursive commands if we're
284 # supposed to be something different.
285 if item.dir:
286 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700287 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700288 current = item.best_subtree
289
290 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700291 item.mode != current[2] or item.selabel != current[4] or \
292 item.capabilities != current[5]:
293 script.SetPermissions("/"+item.name, item.uid, item.gid,
294 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700295
296 for i in item.children:
297 recurse(i, current)
298 else:
299 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700300 item.mode != current[3] or item.selabel != current[4] or \
301 item.capabilities != current[5]:
302 script.SetPermissions("/"+item.name, item.uid, item.gid,
303 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700304
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700305 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700306
307
308def CopySystemFiles(input_zip, output_zip=None,
309 substitute=None):
310 """Copies files underneath system/ in the input zip to the output
311 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800312 list of symlinks. output_zip may be None, in which case the copy is
313 skipped (but the other side effects still happen). substitute is an
314 optional dict of {output filename: contents} to be output instead of
315 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700316 """
317
318 symlinks = []
319
320 for info in input_zip.infolist():
321 if info.filename.startswith("SYSTEM/"):
322 basefilename = info.filename[7:]
323 if IsSymlink(info):
324 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700325 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700326 else:
327 info2 = copy.copy(info)
328 fn = info2.filename = "system/" + basefilename
329 if substitute and fn in substitute and substitute[fn] is None:
330 continue
331 if output_zip is not None:
332 if substitute and fn in substitute:
333 data = substitute[fn]
334 else:
335 data = input_zip.read(info.filename)
336 output_zip.writestr(info2, data)
337 if fn.endswith("/"):
338 Item.Get(fn[:-1], dir=True)
339 else:
340 Item.Get(fn, dir=False)
341
342 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800343 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700344
345
Doug Zongkereef39442009-04-02 12:14:19 -0700346def SignOutput(temp_zip_name, output_zip_name):
347 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
348 pw = key_passwords[OPTIONS.package_key]
349
Doug Zongker951495f2009-08-14 12:44:19 -0700350 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
351 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700352
353
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700354def AppendAssertions(script, info_dict):
355 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700356 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700357
Doug Zongkereef39442009-04-02 12:14:19 -0700358
Doug Zongkerb32161a2012-08-21 10:33:44 -0700359def MakeRecoveryPatch(input_tmp, output_zip, recovery_img, boot_img):
Doug Zongker73ef8252009-07-23 15:12:53 -0700360 """Generate a binary patch that creates the recovery image starting
361 with the boot image. (Most of the space in these images is just the
362 kernel, which is identical for the two, so the resulting patch
363 should be efficient.) Add it to the output zip, along with a shell
364 script that is run from init.rc on first boot to actually do the
365 patching and install the new recovery image.
366
367 recovery_img and boot_img should be File objects for the
Doug Zongker67369982010-07-07 13:53:32 -0700368 corresponding images. info should be the dictionary returned by
369 common.LoadInfoDict() on the input target_files.
Doug Zongker73ef8252009-07-23 15:12:53 -0700370
371 Returns an Item for the shell script, which must be made
372 executable.
373 """
374
Doug Zongkerb32161a2012-08-21 10:33:44 -0700375 diff_program = ["imgdiff"]
376 path = os.path.join(input_tmp, "SYSTEM", "etc", "recovery-resource.dat")
377 if os.path.exists(path):
378 diff_program.append("-b")
379 diff_program.append(path)
380 bonus_args = "-b /system/etc/recovery-resource.dat"
381 else:
382 bonus_args = ""
383
384 d = common.Difference(recovery_img, boot_img, diff_program=diff_program)
Doug Zongker761e6422009-09-25 10:45:39 -0700385 _, _, patch = d.ComputePatch()
Doug Zongkercfd7db62009-10-07 11:35:53 -0700386 common.ZipWriteStr(output_zip, "recovery/recovery-from-boot.p", patch)
Doug Zongker73ef8252009-07-23 15:12:53 -0700387 Item.Get("system/recovery-from-boot.p", dir=False)
388
Doug Zongker96a57e72010-09-26 14:57:41 -0700389 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
390 recovery_type, recovery_device = common.GetTypeAndDevice("/recovery", OPTIONS.info_dict)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700391
Doug Zongker73ef8252009-07-23 15:12:53 -0700392 sh = """#!/system/bin/sh
Doug Zongker0276d182011-12-02 10:46:59 -0800393if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
Doug Zongker73ef8252009-07-23 15:12:53 -0700394 log -t recovery "Installing new recovery image"
Doug Zongkerb32161a2012-08-21 10:33:44 -0700395 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 -0700396else
397 log -t recovery "Recovery image already installed"
398fi
399""" % { 'boot_size': boot_img.size,
400 'boot_sha1': boot_img.sha1,
Doug Zongker73ef8252009-07-23 15:12:53 -0700401 'recovery_size': recovery_img.size,
Doug Zongker67369982010-07-07 13:53:32 -0700402 'recovery_sha1': recovery_img.sha1,
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700403 'boot_type': boot_type,
404 'boot_device': boot_device,
405 'recovery_type': recovery_type,
406 'recovery_device': recovery_device,
Doug Zongkerb32161a2012-08-21 10:33:44 -0700407 'bonus_args': bonus_args,
Doug Zongker67369982010-07-07 13:53:32 -0700408 }
Doug Zongkercfd7db62009-10-07 11:35:53 -0700409 common.ZipWriteStr(output_zip, "recovery/etc/install-recovery.sh", sh)
Doug Zongker73ef8252009-07-23 15:12:53 -0700410 return Item.Get("system/etc/install-recovery.sh", dir=False)
411
412
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700413def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700414 # TODO: how to determine this? We don't know what version it will
415 # be installed on top of. For now, we expect the API just won't
416 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700417 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700418
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700419 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
420 OPTIONS.info_dict),
421 "pre-device": GetBuildProp("ro.product.device",
422 OPTIONS.info_dict),
423 "post-timestamp": GetBuildProp("ro.build.date.utc",
424 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700425 }
426
Doug Zongker05d3dea2009-06-22 11:32:31 -0700427 device_specific = common.DeviceSpecificParams(
428 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700429 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700430 output_zip=output_zip,
431 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700432 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700433 metadata=metadata,
434 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700435
Doug Zongker962069c2009-04-23 11:41:58 -0700436 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700437 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700438 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
439 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700440
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700441 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700442 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800443
444 # Two-step package strategy (in chronological order, which is *not*
445 # the order in which the generated script has things):
446 #
447 # if stage is not "2/3" or "3/3":
448 # write recovery image to boot partition
449 # set stage to "2/3"
450 # reboot to boot partition and restart recovery
451 # else if stage is "2/3":
452 # write recovery image to recovery partition
453 # set stage to "3/3"
454 # reboot to recovery partition and restart recovery
455 # else:
456 # (stage must be "3/3")
457 # set stage to ""
458 # do normal full package installation:
459 # wipe and install system, boot image, etc.
460 # set up system to update recovery partition on first boot
461 # complete script normally (allow recovery to mark itself finished and reboot)
462
463 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
464 OPTIONS.input_tmp, "RECOVERY")
465 if OPTIONS.two_step:
466 if not OPTIONS.info_dict.get("multistage_support", None):
467 assert False, "two-step packages not supported by this build"
468 fs = OPTIONS.info_dict["fstab"]["/misc"]
469 assert fs.fs_type.upper() == "EMMC", \
470 "two-step packages only supported on devices with EMMC /misc partitions"
471 bcb_dev = {"bcb_dev": fs.device}
472 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
473 script.AppendExtra("""
474if get_stage("%(bcb_dev)s", "stage") == "2/3" then
475""" % bcb_dev)
476 script.WriteRawImage("/recovery", "recovery.img")
477 script.AppendExtra("""
478set_stage("%(bcb_dev)s", "3/3");
479reboot_now("%(bcb_dev)s", "recovery");
480else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
481""" % bcb_dev)
482
Doug Zongkere5ff5902012-01-17 10:55:37 -0800483 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700484
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700485 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700486
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700487 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700488 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700489
Kenny Rootf32dc712012-04-08 10:42:34 -0700490 if "selinux_fc" in OPTIONS.info_dict:
491 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500492
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700493 script.FormatPartition("/system")
494 script.Mount("/system")
Doug Zongkercfd7db62009-10-07 11:35:53 -0700495 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700496 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700497
Doug Zongker1807e702012-02-28 12:21:08 -0800498 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700499 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700500
Doug Zongker55d93282011-01-25 17:03:34 -0800501 boot_img = common.GetBootableImage("boot.img", "boot.img",
502 OPTIONS.input_tmp, "BOOT")
Doug Zongkerb32161a2012-08-21 10:33:44 -0700503 MakeRecoveryPatch(OPTIONS.input_tmp, output_zip, recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700504
Doug Zongker283e2a12010-03-15 17:52:32 -0700505 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700506 Item.Get("system").SetPermissions(script)
507
Doug Zongker37974732010-09-16 17:44:38 -0700508 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700509 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700510 script.ShowProgress(0.2, 0)
511
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700512 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700513 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700514
515 script.ShowProgress(0.1, 0)
516 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700517
Doug Zongker1c390a22009-05-14 19:06:36 -0700518 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700519 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700520
Doug Zongker14833602010-02-02 13:12:04 -0800521 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800522
523 if OPTIONS.two_step:
524 script.AppendExtra("""
525set_stage("%(bcb_dev)s", "");
526""" % bcb_dev)
527 script.AppendExtra("else\n")
528 script.WriteRawImage("/boot", "recovery.img")
529 script.AppendExtra("""
530set_stage("%(bcb_dev)s", "2/3");
531reboot_now("%(bcb_dev)s", "");
532endif;
533endif;
534""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700535 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700536 WriteMetadata(metadata, output_zip)
537
Stephen Smalley56882bf2012-02-09 13:36:21 -0500538def WritePolicyConfig(file_context, output_zip):
539 f = open(file_context, 'r');
540 basename = os.path.basename(file_context)
541 common.ZipWriteStr(output_zip, basename, f.read())
542
Doug Zongker2ea21062010-04-28 16:05:21 -0700543
544def WriteMetadata(metadata, output_zip):
545 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
546 "".join(["%s=%s\n" % kv
547 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700548
Doug Zongkereef39442009-04-02 12:14:19 -0700549def LoadSystemFiles(z):
550 """Load all the files from SYSTEM/... in a given target-files
551 ZipFile, and return a dict of {filename: File object}."""
552 out = {}
553 for info in z.infolist():
554 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700555 basefilename = info.filename[7:]
556 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700557 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700558 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800559 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700560
561
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700562def GetBuildProp(prop, info_dict):
563 """Return the fingerprint of the build of a given target-files info_dict."""
564 try:
565 return info_dict.get("build.prop", {})[prop]
566 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700567 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700568
Michael Runge4038aa82013-12-13 18:06:28 -0800569def AddToKnownPaths(filename, known_paths):
570 if filename[-1] == "/":
571 return
572 dirs = filename.split("/")[:-1]
573 while len(dirs) > 0:
574 path = "/".join(dirs)
575 if path in known_paths:
576 break;
577 known_paths.add(path)
578 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700579
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700580def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700581 source_version = OPTIONS.source_info_dict["recovery_api_version"]
582 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700583
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700584 if source_version == 0:
585 print ("WARNING: generating edify script for a source that "
586 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700587 script = edify_generator.EdifyGenerator(source_version,
588 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700589
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700590 metadata = {"pre-device": GetBuildProp("ro.product.device",
591 OPTIONS.source_info_dict),
592 "post-timestamp": GetBuildProp("ro.build.date.utc",
593 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700594 }
595
Doug Zongker05d3dea2009-06-22 11:32:31 -0700596 device_specific = common.DeviceSpecificParams(
597 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800598 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700599 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800600 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700601 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700602 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700603 metadata=metadata,
604 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700605
Doug Zongkereef39442009-04-02 12:14:19 -0700606 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800607 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700608 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800609 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700610
611 verbatim_targets = []
612 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700613 diffs = []
Michael Runge4038aa82013-12-13 18:06:28 -0800614 renames = {}
615 known_paths = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700616 largest_source_size = 0
Michael Runge4038aa82013-12-13 18:06:28 -0800617
618 matching_file_cache = {}
619 for fn, sf in source_data.items():
620 assert fn == sf.name
621 matching_file_cache["path:" + fn] = sf
622 if fn in target_data.keys():
623 AddToKnownPaths(fn, known_paths)
624 # Only allow eligibility for filename/sha matching
625 # if there isn't a perfect path match.
626 if target_data.get(sf.name) is None:
627 matching_file_cache["file:" + fn.split("/")[-1]] = sf
628 matching_file_cache["sha:" + sf.sha1] = sf
629
Doug Zongkereef39442009-04-02 12:14:19 -0700630 for fn in sorted(target_data.keys()):
631 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700632 assert fn == tf.name
Michael Runge4038aa82013-12-13 18:06:28 -0800633 sf = ClosestFileMatch(tf, matching_file_cache, renames)
634 if sf is not None and sf.name != tf.name:
635 print "File has moved from " + sf.name + " to " + tf.name
636 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700637
638 if sf is None or fn in OPTIONS.require_verbatim:
639 # This file should be included verbatim
640 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700641 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700642 print "send", fn, "verbatim"
643 tf.AddToZip(output_zip)
644 verbatim_targets.append((fn, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800645 if fn in target_data.keys():
646 AddToKnownPaths(fn, known_paths)
Doug Zongkereef39442009-04-02 12:14:19 -0700647 elif tf.sha1 != sf.sha1:
648 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700649 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700650 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800651 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700652 pass
653
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700654 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700655
656 for diff in diffs:
657 tf, sf, d = diff.GetPatch()
Michael Runge4038aa82013-12-13 18:06:28 -0800658 path = "/".join(tf.name.split("/")[:-1])
659 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
660 path not in known_paths:
Doug Zongker761e6422009-09-25 10:45:39 -0700661 # patch is almost as big as the file; don't bother patching
Michael Runge4038aa82013-12-13 18:06:28 -0800662 # or a patch + rename cannot take place due to the target
663 # directory not existing
Doug Zongker761e6422009-09-25 10:45:39 -0700664 tf.AddToZip(output_zip)
665 verbatim_targets.append((tf.name, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800666 if sf.name in renames:
667 del renames[sf.name]
668 AddToKnownPaths(tf.name, known_paths)
Doug Zongker761e6422009-09-25 10:45:39 -0700669 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800670 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
671 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700672 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700673
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700674 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
675 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700676 metadata["pre-build"] = source_fp
677 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700678
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700679 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700680 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700681
Doug Zongker55d93282011-01-25 17:03:34 -0800682 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700683 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
684 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800685 target_boot = common.GetBootableImage(
686 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800687 updating_boot = (not OPTIONS.two_step and
688 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700689
Doug Zongker55d93282011-01-25 17:03:34 -0800690 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700691 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
692 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800693 target_recovery = common.GetBootableImage(
694 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700695 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700696
Doug Zongker881dd402009-09-20 14:03:55 -0700697 # Here's how we divide up the progress bar:
698 # 0.1 for verifying the start state (PatchCheck calls)
699 # 0.8 for applying patches (ApplyPatch calls)
700 # 0.1 for unpacking verbatim files, symlinking, and doing the
701 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700702
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700703 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700704 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700705
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800706 # Two-step incremental package strategy (in chronological order,
707 # which is *not* the order in which the generated script has
708 # things):
709 #
710 # if stage is not "2/3" or "3/3":
711 # do verification on current system
712 # write recovery image to boot partition
713 # set stage to "2/3"
714 # reboot to boot partition and restart recovery
715 # else if stage is "2/3":
716 # write recovery image to recovery partition
717 # set stage to "3/3"
718 # reboot to recovery partition and restart recovery
719 # else:
720 # (stage must be "3/3")
721 # perform update:
722 # patch system files, etc.
723 # force full install of new boot image
724 # set up system to update recovery partition on first boot
725 # complete script normally (allow recovery to mark itself finished and reboot)
726
727 if OPTIONS.two_step:
728 if not OPTIONS.info_dict.get("multistage_support", None):
729 assert False, "two-step packages not supported by this build"
730 fs = OPTIONS.info_dict["fstab"]["/misc"]
731 assert fs.fs_type.upper() == "EMMC", \
732 "two-step packages only supported on devices with EMMC /misc partitions"
733 bcb_dev = {"bcb_dev": fs.device}
734 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
735 script.AppendExtra("""
736if get_stage("%(bcb_dev)s", "stage") == "2/3" then
737""" % bcb_dev)
738 script.AppendExtra("sleep(20);\n");
739 script.WriteRawImage("/recovery", "recovery.img")
740 script.AppendExtra("""
741set_stage("%(bcb_dev)s", "3/3");
742reboot_now("%(bcb_dev)s", "recovery");
743else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
744""" % bcb_dev)
745
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700746 script.Print("Verifying current system...")
747
Doug Zongkere5ff5902012-01-17 10:55:37 -0800748 device_specific.IncrementalOTA_VerifyBegin()
749
Doug Zongker881dd402009-09-20 14:03:55 -0700750 script.ShowProgress(0.1, 0)
Michael Runge4038aa82013-12-13 18:06:28 -0800751 total_verify_size = float(sum([i[1].size for i in patch_list]) + 1)
Doug Zongker881dd402009-09-20 14:03:55 -0700752 if updating_boot:
753 total_verify_size += source_boot.size
754 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700755
Michael Runge4038aa82013-12-13 18:06:28 -0800756 for tf, sf, size, patch_sha in patch_list:
757 if tf.name != sf.name:
758 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
759 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700760 so_far += sf.size
761 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700762
Doug Zongker5da317e2009-06-02 13:38:17 -0700763 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700764 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700765 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700766 print "boot target: %d source: %d diff: %d" % (
767 target_boot.size, source_boot.size, len(d))
768
Doug Zongker048e7ca2009-06-15 14:31:53 -0700769 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700770
Doug Zongker96a57e72010-09-26 14:57:41 -0700771 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700772
773 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
774 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700775 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700776 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700777 so_far += source_boot.size
778 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700779
780 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700781 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800782
Doug Zongker05d3dea2009-06-22 11:32:31 -0700783 device_specific.IncrementalOTA_VerifyEnd()
784
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800785 if OPTIONS.two_step:
786 script.WriteRawImage("/boot", "recovery.img")
787 script.AppendExtra("""
788set_stage("%(bcb_dev)s", "2/3");
789reboot_now("%(bcb_dev)s", "");
790else
791""" % bcb_dev)
792
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700793 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700794
Doug Zongkere5ff5902012-01-17 10:55:37 -0800795 device_specific.IncrementalOTA_InstallBegin()
796
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800797 if OPTIONS.two_step:
798 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
799 script.WriteRawImage("/boot", "boot.img")
800 print "writing full boot image (forced by two-step mode)"
801
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700802 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700803 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700804 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700805
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700806 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700807 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
808 ["/"+i for i in sorted(source_data)
Michael Runge4038aa82013-12-13 18:06:28 -0800809 if i not in target_data and
810 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700811 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700812
Doug Zongker881dd402009-09-20 14:03:55 -0700813 script.ShowProgress(0.8, 0)
814 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
815 if updating_boot:
816 total_patch_size += target_boot.size
817 so_far = 0
818
819 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700820 deferred_patch_list = []
821 for item in patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800822 tf, sf, size, _ = item
Doug Zongkere92f15a2011-08-26 13:46:40 -0700823 if tf.name == "system/build.prop":
824 deferred_patch_list.append(item)
825 continue
Michael Runge4038aa82013-12-13 18:06:28 -0800826 if (sf.name != tf.name):
827 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
828 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700829 so_far += tf.size
830 script.SetProgress(so_far / total_patch_size)
831
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800832 if not OPTIONS.two_step:
833 if updating_boot:
834 # Produce the boot image by applying a patch to the current
835 # contents of the boot partition, and write it back to the
836 # partition.
837 script.Print("Patching boot image...")
838 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
839 % (boot_type, boot_device,
840 source_boot.size, source_boot.sha1,
841 target_boot.size, target_boot.sha1),
842 "-",
843 target_boot.size, target_boot.sha1,
844 source_boot.sha1, "patch/boot.img.p")
845 so_far += target_boot.size
846 script.SetProgress(so_far / total_patch_size)
847 print "boot image changed; including."
848 else:
849 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -0700850
851 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700852 # Recovery is generated as a patch using both the boot image
853 # (which contains the same linux kernel as recovery) and the file
854 # /system/etc/recovery-resource.dat (which contains all the images
855 # used in the recovery UI) as sources. This lets us minimize the
856 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700857 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700858 # For older builds where recovery-resource.dat is not present, we
859 # use only the boot image as the source.
860
861 MakeRecoveryPatch(OPTIONS.target_tmp, output_zip,
862 target_recovery, target_boot)
Doug Zongker42265392010-02-12 10:21:00 -0800863 script.DeleteFiles(["/system/recovery-from-boot.p",
864 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700865 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700866 else:
867 print "recovery image unchanged; skipping."
868
Doug Zongker881dd402009-09-20 14:03:55 -0700869 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700870
Doug Zongker1807e702012-02-28 12:21:08 -0800871 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700872
873 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700874 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700875 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700876 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700877
878 # Note that this call will mess up the tree of Items, so make sure
879 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800880 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700881 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
882
883 # Delete all the symlinks in source that aren't in target. This
884 # needs to happen before verbatim files are unpacked, in case a
885 # symlink in the source is replaced by a real file in the target.
886 to_delete = []
887 for dest, link in source_symlinks:
888 if link not in target_symlinks_d:
889 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700890 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700891
892 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700893 script.Print("Unpacking new files...")
894 script.UnpackPackageDir("system", "/system")
895
Doug Zongker42265392010-02-12 10:21:00 -0800896 if updating_recovery:
897 script.Print("Unpacking new recovery...")
898 script.UnpackPackageDir("recovery", "/system")
899
Michael Runge4038aa82013-12-13 18:06:28 -0800900 if len(renames) > 0:
901 script.Print("Renaming files...")
902
903 for src in renames:
904 print "Renaming " + src + " to " + renames[src].name
905 script.RenameFile(src, renames[src].name)
906
Doug Zongker05d3dea2009-06-22 11:32:31 -0700907 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700908
909 # Create all the symlinks that don't already exist, or point to
910 # somewhere different than what we want. Delete each symlink before
911 # creating it, since the 'symlink' command won't overwrite.
912 to_create = []
913 for dest, link in target_symlinks:
914 if link in source_symlinks_d:
915 if dest != source_symlinks_d[link]:
916 to_create.append((dest, link))
917 else:
918 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700919 script.DeleteFiles([i[1] for i in to_create])
920 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700921
922 # Now that the symlinks are created, we can set all the
923 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700924 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700925
Doug Zongker881dd402009-09-20 14:03:55 -0700926 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700927 device_specific.IncrementalOTA_InstallEnd()
928
Doug Zongker1c390a22009-05-14 19:06:36 -0700929 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700930 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700931
Doug Zongkere92f15a2011-08-26 13:46:40 -0700932 # Patch the build.prop file last, so if something fails but the
933 # device can still come up, it appears to be the old build and will
934 # get set the OTA package again to retry.
935 script.Print("Patching remaining system files...")
936 for item in deferred_patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800937 tf, sf, size, _ = item
938 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700939 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700940
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800941 if OPTIONS.two_step:
942 script.AppendExtra("""
943set_stage("%(bcb_dev)s", "");
944endif;
945endif;
946""" % bcb_dev)
947
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700948 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700949 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700950
951
952def main(argv):
953
954 def option_handler(o, a):
955 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700956 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700957 elif o in ("-k", "--package_key"):
958 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700959 elif o in ("-i", "--incremental_from"):
960 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700961 elif o in ("-w", "--wipe_user_data"):
962 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700963 elif o in ("-n", "--no_prereq"):
964 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700965 elif o in ("-e", "--extra_script"):
966 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700967 elif o in ("-a", "--aslr_mode"):
968 if a in ("on", "On", "true", "True", "yes", "Yes"):
969 OPTIONS.aslr_mode = True
970 else:
971 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200972 elif o in ("-t", "--worker_threads"):
973 if a.isdigit():
974 OPTIONS.worker_threads = int(a)
975 else:
976 raise ValueError("Cannot parse value %r for option %r - only "
977 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800978 elif o in ("-2", "--two_step"):
979 OPTIONS.two_step = True
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900980 elif o in ("--no_signing"):
981 OPTIONS.no_signing = True
Doug Zongkereef39442009-04-02 12:14:19 -0700982 else:
983 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700984 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700985
986 args = common.ParseOptions(argv, __doc__,
Martin Blumenstingl374e1142014-05-31 20:42:55 +0200987 extra_opts="b:k:i:d:wne:t:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -0700988 extra_long_opts=["board_config=",
989 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700990 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700991 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700992 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700993 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700994 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700995 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800996 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900997 "no_signing",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700998 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700999 extra_option_handler=option_handler)
1000
1001 if len(args) != 2:
1002 common.Usage(__doc__)
1003 sys.exit(1)
1004
Doug Zongker1c390a22009-05-14 19:06:36 -07001005 if OPTIONS.extra_script is not None:
1006 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1007
Doug Zongkereef39442009-04-02 12:14:19 -07001008 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001009 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001010
Doug Zongkereef39442009-04-02 12:14:19 -07001011 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001012 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001013
1014 # If this image was originally labelled with SELinux contexts, make sure we
1015 # also apply the labels in our new image. During building, the "file_contexts"
1016 # is in the out/ directory tree, but for repacking from target-files.zip it's
1017 # in the root directory of the ramdisk.
1018 if "selinux_fc" in OPTIONS.info_dict:
1019 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1020 "file_contexts")
1021
Doug Zongker37974732010-09-16 17:44:38 -07001022 if OPTIONS.verbose:
1023 print "--- target info ---"
1024 common.DumpInfoDict(OPTIONS.info_dict)
1025
1026 if OPTIONS.device_specific is None:
1027 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1028 if OPTIONS.device_specific is not None:
1029 OPTIONS.device_specific = os.path.normpath(OPTIONS.device_specific)
1030 print "using device-specific extensions in", OPTIONS.device_specific
1031
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001032 if OPTIONS.no_signing:
1033 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1034 else:
1035 temp_zip_file = tempfile.NamedTemporaryFile()
1036 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1037 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001038
1039 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001040 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001041 if OPTIONS.package_key is None:
1042 OPTIONS.package_key = OPTIONS.info_dict.get(
1043 "default_system_dev_certificate",
1044 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001045 else:
1046 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001047 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001048 OPTIONS.target_info_dict = OPTIONS.info_dict
1049 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001050 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001051 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001052 "default_system_dev_certificate",
1053 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001054 if OPTIONS.verbose:
1055 print "--- source info ---"
1056 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001057 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001058
1059 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001060
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001061 if not OPTIONS.no_signing:
1062 SignOutput(temp_zip_file.name, args[1])
1063 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001064
1065 common.Cleanup()
1066
1067 print "done."
1068
1069
1070if __name__ == '__main__':
1071 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001072 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001073 main(sys.argv[1:])
1074 except common.ExternalError, e:
1075 print
1076 print " ERROR: %s" % (e,)
1077 print
1078 sys.exit(1)