blob: 60ccd0fa52a653d5d2dffb7f48bf3932f4e47687 [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
Doug Zongkercf6d5a92014-02-18 10:57:07 -080064if sys.hexversion < 0x02070000:
65 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -070066 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 Zongker01ce19c2014-02-04 13:48:15 -080083import img_from_target_files
Doug Zongkerc494d7c2009-06-18 08:43:44 -070084import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070085
86OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -070087OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -070088OPTIONS.incremental_source = None
89OPTIONS.require_verbatim = set()
90OPTIONS.prohibit_verbatim = set(("system/build.prop",))
91OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070092OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070093OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070094OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070095OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070096OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -080097OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +090098OPTIONS.no_signing = False
Doug Zongkereef39442009-04-02 12:14:19 -070099
100def MostPopularKey(d, default):
101 """Given a dict, return the key corresponding to the largest
102 value. Returns 'default' if the dict is empty."""
103 x = [(v, k) for (k, v) in d.iteritems()]
104 if not x: return default
105 x.sort()
106 return x[-1][1]
107
108
109def IsSymlink(info):
110 """Return true if the zipfile.ZipInfo object passed in represents a
111 symlink."""
112 return (info.external_attr >> 16) == 0120777
113
Hristo Bojinov96be7202010-08-02 10:26:17 -0700114def IsRegular(info):
115 """Return true if the zipfile.ZipInfo object passed in represents a
116 symlink."""
117 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700118
Michael Runge4038aa82013-12-13 18:06:28 -0800119def ClosestFileMatch(src, tgtfiles, existing):
120 """Returns the closest file match between a source file and list
121 of potential matches. The exact filename match is preferred,
122 then the sha1 is searched for, and finally a file with the same
123 basename is evaluated. Rename support in the updater-binary is
124 required for the latter checks to be used."""
125
126 result = tgtfiles.get("path:" + src.name)
127 if result is not None:
128 return result
129
130 if not OPTIONS.target_info_dict.get("update_rename_support", False):
131 return None
132
133 if src.size < 1000:
134 return None
135
136 result = tgtfiles.get("sha1:" + src.sha1)
137 if result is not None and existing.get(result.name) is None:
138 return result
139 result = tgtfiles.get("file:" + src.name.split("/")[-1])
140 if result is not None and existing.get(result.name) is None:
141 return result
142 return None
143
Doug Zongkereef39442009-04-02 12:14:19 -0700144class Item:
145 """Items represent the metadata (user, group, mode) of files and
146 directories in the system image."""
147 ITEMS = {}
148 def __init__(self, name, dir=False):
149 self.name = name
150 self.uid = None
151 self.gid = None
152 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700153 self.selabel = None
154 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700155 self.dir = dir
156
157 if name:
158 self.parent = Item.Get(os.path.dirname(name), dir=True)
159 self.parent.children.append(self)
160 else:
161 self.parent = None
162 if dir:
163 self.children = []
164
165 def Dump(self, indent=0):
166 if self.uid is not None:
167 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
168 else:
169 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
170 if self.dir:
171 print "%s%s" % (" "*indent, self.descendants)
172 print "%s%s" % (" "*indent, self.best_subtree)
173 for i in self.children:
174 i.Dump(indent=indent+1)
175
176 @classmethod
177 def Get(cls, name, dir=False):
178 if name not in cls.ITEMS:
179 cls.ITEMS[name] = Item(name, dir=dir)
180 return cls.ITEMS[name]
181
182 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700183 def GetMetadata(cls, input_zip):
184
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700185 # The target_files contains a record of what the uid,
186 # gid, and mode are supposed to be.
187 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700188
189 for line in output.split("\n"):
190 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700191 columns = line.split()
192 name, uid, gid, mode = columns[:4]
193 selabel = None
194 capabilities = None
195
196 # After the first 4 columns, there are a series of key=value
197 # pairs. Extract out the fields we care about.
198 for element in columns[4:]:
199 key, value = element.split("=")
200 if key == "selabel":
201 selabel = value
202 if key == "capabilities":
203 capabilities = value
204
Doug Zongker283e2a12010-03-15 17:52:32 -0700205 i = cls.ITEMS.get(name, None)
206 if i is not None:
207 i.uid = int(uid)
208 i.gid = int(gid)
209 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700210 i.selabel = selabel
211 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700212 if i.dir:
213 i.children.sort(key=lambda i: i.name)
214
215 # set metadata for the files generated by this script.
216 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700217 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700218 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700219 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700220
221 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700222 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
223 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700224 set_perm to correctly chown/chmod all the files to their desired
225 values. Recursively calls itself for all descendants.
226
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700227 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700228 all descendants of this node. (dmode or fmode may be None.) Also
229 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700230 dmode, fmode, selabel, capabilities) tuple that will match the most
231 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700232 """
233
234 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700235 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700236 for i in self.children:
237 if i.dir:
238 for k, v in i.CountChildMetadata().iteritems():
239 d[k] = d.get(k, 0) + v
240 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700241 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700242 d[k] = d.get(k, 0) + 1
243
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700244 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
245 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700246
247 # First, find the (uid, gid) pair that matches the most
248 # descendants.
249 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700250 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700251 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
252 ug = MostPopularKey(ug, (0, 0))
253
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700254 # Now find the dmode, fmode, selabel, and capabilities that match
255 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700256 best_dmode = (0, 0755)
257 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700258 best_selabel = (0, None)
259 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700260 for k, count in d.iteritems():
261 if k[:2] != ug: continue
262 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
263 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700264 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
265 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
266 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700267
268 return d
269
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700270 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700271 """Append set_perm/set_perm_recursive commands to 'script' to
272 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700273 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700274
275 self.CountChildMetadata()
276
277 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700278 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700279 # item (and all its children) have already been set to. We only
280 # need to issue set_perm/set_perm_recursive commands if we're
281 # supposed to be something different.
282 if item.dir:
283 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700284 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700285 current = item.best_subtree
286
287 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700288 item.mode != current[2] or item.selabel != current[4] or \
289 item.capabilities != current[5]:
290 script.SetPermissions("/"+item.name, item.uid, item.gid,
291 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700292
293 for i in item.children:
294 recurse(i, current)
295 else:
296 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700297 item.mode != current[3] or item.selabel != current[4] or \
298 item.capabilities != current[5]:
299 script.SetPermissions("/"+item.name, item.uid, item.gid,
300 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700301
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700302 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700303
304
305def CopySystemFiles(input_zip, output_zip=None,
306 substitute=None):
307 """Copies files underneath system/ in the input zip to the output
308 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800309 list of symlinks. output_zip may be None, in which case the copy is
310 skipped (but the other side effects still happen). substitute is an
311 optional dict of {output filename: contents} to be output instead of
312 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700313 """
314
315 symlinks = []
316
317 for info in input_zip.infolist():
318 if info.filename.startswith("SYSTEM/"):
319 basefilename = info.filename[7:]
320 if IsSymlink(info):
321 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700322 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700323 else:
324 info2 = copy.copy(info)
325 fn = info2.filename = "system/" + basefilename
326 if substitute and fn in substitute and substitute[fn] is None:
327 continue
328 if output_zip is not None:
329 if substitute and fn in substitute:
330 data = substitute[fn]
331 else:
332 data = input_zip.read(info.filename)
333 output_zip.writestr(info2, data)
334 if fn.endswith("/"):
335 Item.Get(fn[:-1], dir=True)
336 else:
337 Item.Get(fn, dir=False)
338
339 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800340 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700341
342
Doug Zongkereef39442009-04-02 12:14:19 -0700343def SignOutput(temp_zip_name, output_zip_name):
344 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
345 pw = key_passwords[OPTIONS.package_key]
346
Doug Zongker951495f2009-08-14 12:44:19 -0700347 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
348 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700349
350
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700351def AppendAssertions(script, info_dict):
352 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700353 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700354
Doug Zongkereef39442009-04-02 12:14:19 -0700355
Doug Zongkerc9253822014-02-04 12:17:58 -0800356def HasRecoveryPatch(target_files_zip):
357 try:
358 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
359 return True
360 except KeyError:
361 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700362
363
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700364def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700365 # TODO: how to determine this? We don't know what version it will
366 # be installed on top of. For now, we expect the API just won't
367 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700368 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700369
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700370 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
371 OPTIONS.info_dict),
372 "pre-device": GetBuildProp("ro.product.device",
373 OPTIONS.info_dict),
374 "post-timestamp": GetBuildProp("ro.build.date.utc",
375 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700376 }
377
Doug Zongker05d3dea2009-06-22 11:32:31 -0700378 device_specific = common.DeviceSpecificParams(
379 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700380 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700381 output_zip=output_zip,
382 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700383 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700384 metadata=metadata,
385 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700386
Doug Zongkerc9253822014-02-04 12:17:58 -0800387 has_recovery_patch = HasRecoveryPatch(input_zip)
388
Doug Zongker962069c2009-04-23 11:41:58 -0700389 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700390 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700391 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
392 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700393
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700394 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700395 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800396
397 # Two-step package strategy (in chronological order, which is *not*
398 # the order in which the generated script has things):
399 #
400 # if stage is not "2/3" or "3/3":
401 # write recovery image to boot partition
402 # set stage to "2/3"
403 # reboot to boot partition and restart recovery
404 # else if stage is "2/3":
405 # write recovery image to recovery partition
406 # set stage to "3/3"
407 # reboot to recovery partition and restart recovery
408 # else:
409 # (stage must be "3/3")
410 # set stage to ""
411 # do normal full package installation:
412 # wipe and install system, boot image, etc.
413 # set up system to update recovery partition on first boot
414 # complete script normally (allow recovery to mark itself finished and reboot)
415
416 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
417 OPTIONS.input_tmp, "RECOVERY")
418 if OPTIONS.two_step:
419 if not OPTIONS.info_dict.get("multistage_support", None):
420 assert False, "two-step packages not supported by this build"
421 fs = OPTIONS.info_dict["fstab"]["/misc"]
422 assert fs.fs_type.upper() == "EMMC", \
423 "two-step packages only supported on devices with EMMC /misc partitions"
424 bcb_dev = {"bcb_dev": fs.device}
425 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
426 script.AppendExtra("""
427if get_stage("%(bcb_dev)s", "stage") == "2/3" then
428""" % bcb_dev)
429 script.WriteRawImage("/recovery", "recovery.img")
430 script.AppendExtra("""
431set_stage("%(bcb_dev)s", "3/3");
432reboot_now("%(bcb_dev)s", "recovery");
433else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
434""" % bcb_dev)
435
Doug Zongkere5ff5902012-01-17 10:55:37 -0800436 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700437
Doug Zongker01ce19c2014-02-04 13:48:15 -0800438 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700439
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700440 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800441 system_progress -= 0.1
442 script.ShowProgress(0.1, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700443 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700444
Kenny Rootf32dc712012-04-08 10:42:34 -0700445 if "selinux_fc" in OPTIONS.info_dict:
446 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500447
Doug Zongker01ce19c2014-02-04 13:48:15 -0800448 script.ShowProgress(system_progress, 30)
449 if has_recovery_patch:
450 img_from_target_files.AddSystem(output_zip, sparse=False)
451 script.WriteRawImage("/system", "system.img")
452 else:
453 script.FormatPartition("/system")
454 script.Mount("/system")
455 if not has_recovery_patch:
456 script.UnpackPackageDir("recovery", "/system")
457 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700458
Doug Zongker01ce19c2014-02-04 13:48:15 -0800459 symlinks = CopySystemFiles(input_zip, output_zip)
460 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700461
Doug Zongker55d93282011-01-25 17:03:34 -0800462 boot_img = common.GetBootableImage("boot.img", "boot.img",
463 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800464
465 if not has_recovery_patch:
466 def output_sink(fn, data):
467 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
468 Item.Get("system/" + fn, dir=False)
469
470 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
471 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700472
Doug Zongker01ce19c2014-02-04 13:48:15 -0800473 Item.GetMetadata(input_zip)
474 Item.Get("system").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700475
Doug Zongker37974732010-09-16 17:44:38 -0700476 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700477 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700478
Doug Zongker01ce19c2014-02-04 13:48:15 -0800479 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700480 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700481
Doug Zongker01ce19c2014-02-04 13:48:15 -0800482 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700483 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700484
Doug Zongker1c390a22009-05-14 19:06:36 -0700485 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700486 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700487
Doug Zongker14833602010-02-02 13:12:04 -0800488 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800489
490 if OPTIONS.two_step:
491 script.AppendExtra("""
492set_stage("%(bcb_dev)s", "");
493""" % bcb_dev)
494 script.AppendExtra("else\n")
495 script.WriteRawImage("/boot", "recovery.img")
496 script.AppendExtra("""
497set_stage("%(bcb_dev)s", "2/3");
498reboot_now("%(bcb_dev)s", "");
499endif;
500endif;
501""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700502 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700503 WriteMetadata(metadata, output_zip)
504
Stephen Smalley56882bf2012-02-09 13:36:21 -0500505def WritePolicyConfig(file_context, output_zip):
506 f = open(file_context, 'r');
507 basename = os.path.basename(file_context)
508 common.ZipWriteStr(output_zip, basename, f.read())
509
Doug Zongker2ea21062010-04-28 16:05:21 -0700510
511def WriteMetadata(metadata, output_zip):
512 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
513 "".join(["%s=%s\n" % kv
514 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700515
Doug Zongkereef39442009-04-02 12:14:19 -0700516def LoadSystemFiles(z):
517 """Load all the files from SYSTEM/... in a given target-files
518 ZipFile, and return a dict of {filename: File object}."""
519 out = {}
520 for info in z.infolist():
521 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700522 basefilename = info.filename[7:]
523 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700524 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700525 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800526 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700527
528
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700529def GetBuildProp(prop, info_dict):
530 """Return the fingerprint of the build of a given target-files info_dict."""
531 try:
532 return info_dict.get("build.prop", {})[prop]
533 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700534 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700535
Michael Runge4038aa82013-12-13 18:06:28 -0800536def AddToKnownPaths(filename, known_paths):
537 if filename[-1] == "/":
538 return
539 dirs = filename.split("/")[:-1]
540 while len(dirs) > 0:
541 path = "/".join(dirs)
542 if path in known_paths:
543 break;
544 known_paths.add(path)
545 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700546
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700547def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700548 source_version = OPTIONS.source_info_dict["recovery_api_version"]
549 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700550
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700551 if source_version == 0:
552 print ("WARNING: generating edify script for a source that "
553 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700554 script = edify_generator.EdifyGenerator(source_version,
555 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700556
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700557 metadata = {"pre-device": GetBuildProp("ro.product.device",
558 OPTIONS.source_info_dict),
559 "post-timestamp": GetBuildProp("ro.build.date.utc",
560 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700561 }
562
Doug Zongker05d3dea2009-06-22 11:32:31 -0700563 device_specific = common.DeviceSpecificParams(
564 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800565 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700566 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800567 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700568 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700569 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700570 metadata=metadata,
571 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700572
Doug Zongkereef39442009-04-02 12:14:19 -0700573 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800574 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700575 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800576 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700577
Doug Zongkerc9253822014-02-04 12:17:58 -0800578 target_has_recovery_patch = HasRecoveryPatch(target_zip)
579
Doug Zongkereef39442009-04-02 12:14:19 -0700580 verbatim_targets = []
581 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700582 diffs = []
Michael Runge4038aa82013-12-13 18:06:28 -0800583 renames = {}
584 known_paths = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700585 largest_source_size = 0
Michael Runge4038aa82013-12-13 18:06:28 -0800586
587 matching_file_cache = {}
588 for fn, sf in source_data.items():
589 assert fn == sf.name
590 matching_file_cache["path:" + fn] = sf
591 if fn in target_data.keys():
592 AddToKnownPaths(fn, known_paths)
593 # Only allow eligibility for filename/sha matching
594 # if there isn't a perfect path match.
595 if target_data.get(sf.name) is None:
596 matching_file_cache["file:" + fn.split("/")[-1]] = sf
597 matching_file_cache["sha:" + sf.sha1] = sf
598
Doug Zongkereef39442009-04-02 12:14:19 -0700599 for fn in sorted(target_data.keys()):
600 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700601 assert fn == tf.name
Michael Runge4038aa82013-12-13 18:06:28 -0800602 sf = ClosestFileMatch(tf, matching_file_cache, renames)
603 if sf is not None and sf.name != tf.name:
604 print "File has moved from " + sf.name + " to " + tf.name
605 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700606
607 if sf is None or fn in OPTIONS.require_verbatim:
608 # This file should be included verbatim
609 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700610 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700611 print "send", fn, "verbatim"
612 tf.AddToZip(output_zip)
613 verbatim_targets.append((fn, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800614 if fn in target_data.keys():
615 AddToKnownPaths(fn, known_paths)
Doug Zongkereef39442009-04-02 12:14:19 -0700616 elif tf.sha1 != sf.sha1:
617 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700618 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700619 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800620 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700621 pass
622
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700623 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700624
625 for diff in diffs:
626 tf, sf, d = diff.GetPatch()
Michael Runge4038aa82013-12-13 18:06:28 -0800627 path = "/".join(tf.name.split("/")[:-1])
628 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
629 path not in known_paths:
Doug Zongker761e6422009-09-25 10:45:39 -0700630 # patch is almost as big as the file; don't bother patching
Michael Runge4038aa82013-12-13 18:06:28 -0800631 # or a patch + rename cannot take place due to the target
632 # directory not existing
Doug Zongker761e6422009-09-25 10:45:39 -0700633 tf.AddToZip(output_zip)
634 verbatim_targets.append((tf.name, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800635 if sf.name in renames:
636 del renames[sf.name]
637 AddToKnownPaths(tf.name, known_paths)
Doug Zongker761e6422009-09-25 10:45:39 -0700638 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800639 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
640 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700641 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700642
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700643 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
644 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700645 metadata["pre-build"] = source_fp
646 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700647
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700648 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700649 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700650
Doug Zongker55d93282011-01-25 17:03:34 -0800651 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700652 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
653 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800654 target_boot = common.GetBootableImage(
655 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800656 updating_boot = (not OPTIONS.two_step and
657 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700658
Doug Zongker55d93282011-01-25 17:03:34 -0800659 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700660 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
661 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800662 target_recovery = common.GetBootableImage(
663 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700664 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700665
Doug Zongker881dd402009-09-20 14:03:55 -0700666 # Here's how we divide up the progress bar:
667 # 0.1 for verifying the start state (PatchCheck calls)
668 # 0.8 for applying patches (ApplyPatch calls)
669 # 0.1 for unpacking verbatim files, symlinking, and doing the
670 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700671
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700672 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700673 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700674
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800675 # Two-step incremental package strategy (in chronological order,
676 # which is *not* the order in which the generated script has
677 # things):
678 #
679 # if stage is not "2/3" or "3/3":
680 # do verification on current system
681 # write recovery image to boot partition
682 # set stage to "2/3"
683 # reboot to boot partition and restart recovery
684 # else if stage is "2/3":
685 # write recovery image to recovery partition
686 # set stage to "3/3"
687 # reboot to recovery partition and restart recovery
688 # else:
689 # (stage must be "3/3")
690 # perform update:
691 # patch system files, etc.
692 # force full install of new boot image
693 # set up system to update recovery partition on first boot
694 # complete script normally (allow recovery to mark itself finished and reboot)
695
696 if OPTIONS.two_step:
697 if not OPTIONS.info_dict.get("multistage_support", None):
698 assert False, "two-step packages not supported by this build"
699 fs = OPTIONS.info_dict["fstab"]["/misc"]
700 assert fs.fs_type.upper() == "EMMC", \
701 "two-step packages only supported on devices with EMMC /misc partitions"
702 bcb_dev = {"bcb_dev": fs.device}
703 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
704 script.AppendExtra("""
705if get_stage("%(bcb_dev)s", "stage") == "2/3" then
706""" % bcb_dev)
707 script.AppendExtra("sleep(20);\n");
708 script.WriteRawImage("/recovery", "recovery.img")
709 script.AppendExtra("""
710set_stage("%(bcb_dev)s", "3/3");
711reboot_now("%(bcb_dev)s", "recovery");
712else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
713""" % bcb_dev)
714
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700715 script.Print("Verifying current system...")
716
Doug Zongkere5ff5902012-01-17 10:55:37 -0800717 device_specific.IncrementalOTA_VerifyBegin()
718
Doug Zongker881dd402009-09-20 14:03:55 -0700719 script.ShowProgress(0.1, 0)
Michael Runge4038aa82013-12-13 18:06:28 -0800720 total_verify_size = float(sum([i[1].size for i in patch_list]) + 1)
Doug Zongker881dd402009-09-20 14:03:55 -0700721 if updating_boot:
722 total_verify_size += source_boot.size
723 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700724
Michael Runge4038aa82013-12-13 18:06:28 -0800725 for tf, sf, size, patch_sha in patch_list:
726 if tf.name != sf.name:
727 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
728 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700729 so_far += sf.size
730 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700731
Doug Zongker5da317e2009-06-02 13:38:17 -0700732 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700733 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700734 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700735 print "boot target: %d source: %d diff: %d" % (
736 target_boot.size, source_boot.size, len(d))
737
Doug Zongker048e7ca2009-06-15 14:31:53 -0700738 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700739
Doug Zongker96a57e72010-09-26 14:57:41 -0700740 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700741
742 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
743 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700744 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700745 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700746 so_far += source_boot.size
747 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700748
749 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700750 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800751
Doug Zongker05d3dea2009-06-22 11:32:31 -0700752 device_specific.IncrementalOTA_VerifyEnd()
753
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800754 if OPTIONS.two_step:
755 script.WriteRawImage("/boot", "recovery.img")
756 script.AppendExtra("""
757set_stage("%(bcb_dev)s", "2/3");
758reboot_now("%(bcb_dev)s", "");
759else
760""" % bcb_dev)
761
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700762 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700763
Doug Zongkere5ff5902012-01-17 10:55:37 -0800764 device_specific.IncrementalOTA_InstallBegin()
765
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800766 if OPTIONS.two_step:
767 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
768 script.WriteRawImage("/boot", "boot.img")
769 print "writing full boot image (forced by two-step mode)"
770
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700771 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700772 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700773 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700774
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700775 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700776 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
777 ["/"+i for i in sorted(source_data)
Michael Runge4038aa82013-12-13 18:06:28 -0800778 if i not in target_data and
779 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700780 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700781
Doug Zongker881dd402009-09-20 14:03:55 -0700782 script.ShowProgress(0.8, 0)
783 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
784 if updating_boot:
785 total_patch_size += target_boot.size
786 so_far = 0
787
788 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700789 deferred_patch_list = []
790 for item in patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800791 tf, sf, size, _ = item
Doug Zongkere92f15a2011-08-26 13:46:40 -0700792 if tf.name == "system/build.prop":
793 deferred_patch_list.append(item)
794 continue
Michael Runge4038aa82013-12-13 18:06:28 -0800795 if (sf.name != tf.name):
796 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
797 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700798 so_far += tf.size
799 script.SetProgress(so_far / total_patch_size)
800
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800801 if not OPTIONS.two_step:
802 if updating_boot:
803 # Produce the boot image by applying a patch to the current
804 # contents of the boot partition, and write it back to the
805 # partition.
806 script.Print("Patching boot image...")
807 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
808 % (boot_type, boot_device,
809 source_boot.size, source_boot.sha1,
810 target_boot.size, target_boot.sha1),
811 "-",
812 target_boot.size, target_boot.sha1,
813 source_boot.sha1, "patch/boot.img.p")
814 so_far += target_boot.size
815 script.SetProgress(so_far / total_patch_size)
816 print "boot image changed; including."
817 else:
818 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -0700819
820 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700821 # Recovery is generated as a patch using both the boot image
822 # (which contains the same linux kernel as recovery) and the file
823 # /system/etc/recovery-resource.dat (which contains all the images
824 # used in the recovery UI) as sources. This lets us minimize the
825 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700826 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700827 # For older builds where recovery-resource.dat is not present, we
828 # use only the boot image as the source.
829
Doug Zongkerc9253822014-02-04 12:17:58 -0800830 if not target_has_recovery_patch:
831 def output_sink(fn, data):
832 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
833 Item.Get("system/" + fn, dir=False)
834
835 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
836 target_recovery, target_boot)
837 script.DeleteFiles(["/system/recovery-from-boot.p",
838 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700839 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700840 else:
841 print "recovery image unchanged; skipping."
842
Doug Zongker881dd402009-09-20 14:03:55 -0700843 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700844
Doug Zongker1807e702012-02-28 12:21:08 -0800845 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700846
847 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700848 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700849 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700850 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700851
852 # Note that this call will mess up the tree of Items, so make sure
853 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800854 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700855 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
856
857 # Delete all the symlinks in source that aren't in target. This
858 # needs to happen before verbatim files are unpacked, in case a
859 # symlink in the source is replaced by a real file in the target.
860 to_delete = []
861 for dest, link in source_symlinks:
862 if link not in target_symlinks_d:
863 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700864 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700865
866 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700867 script.Print("Unpacking new files...")
868 script.UnpackPackageDir("system", "/system")
869
Doug Zongkerc9253822014-02-04 12:17:58 -0800870 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -0800871 script.Print("Unpacking new recovery...")
872 script.UnpackPackageDir("recovery", "/system")
873
Michael Runge4038aa82013-12-13 18:06:28 -0800874 if len(renames) > 0:
875 script.Print("Renaming files...")
876
877 for src in renames:
878 print "Renaming " + src + " to " + renames[src].name
879 script.RenameFile(src, renames[src].name)
880
Doug Zongker05d3dea2009-06-22 11:32:31 -0700881 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700882
883 # Create all the symlinks that don't already exist, or point to
884 # somewhere different than what we want. Delete each symlink before
885 # creating it, since the 'symlink' command won't overwrite.
886 to_create = []
887 for dest, link in target_symlinks:
888 if link in source_symlinks_d:
889 if dest != source_symlinks_d[link]:
890 to_create.append((dest, link))
891 else:
892 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700893 script.DeleteFiles([i[1] for i in to_create])
894 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700895
896 # Now that the symlinks are created, we can set all the
897 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700898 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700899
Doug Zongker881dd402009-09-20 14:03:55 -0700900 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700901 device_specific.IncrementalOTA_InstallEnd()
902
Doug Zongker1c390a22009-05-14 19:06:36 -0700903 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700904 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700905
Doug Zongkere92f15a2011-08-26 13:46:40 -0700906 # Patch the build.prop file last, so if something fails but the
907 # device can still come up, it appears to be the old build and will
908 # get set the OTA package again to retry.
909 script.Print("Patching remaining system files...")
910 for item in deferred_patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800911 tf, sf, size, _ = item
912 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700913 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700914
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800915 if OPTIONS.two_step:
916 script.AppendExtra("""
917set_stage("%(bcb_dev)s", "");
918endif;
919endif;
920""" % bcb_dev)
921
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700922 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700923 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700924
925
926def main(argv):
927
928 def option_handler(o, a):
929 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700930 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700931 elif o in ("-k", "--package_key"):
932 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700933 elif o in ("-i", "--incremental_from"):
934 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700935 elif o in ("-w", "--wipe_user_data"):
936 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700937 elif o in ("-n", "--no_prereq"):
938 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700939 elif o in ("-e", "--extra_script"):
940 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700941 elif o in ("-a", "--aslr_mode"):
942 if a in ("on", "On", "true", "True", "yes", "Yes"):
943 OPTIONS.aslr_mode = True
944 else:
945 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700946 elif o in ("--worker_threads"):
947 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800948 elif o in ("-2", "--two_step"):
949 OPTIONS.two_step = True
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900950 elif o in ("--no_signing"):
951 OPTIONS.no_signing = True
Doug Zongkereef39442009-04-02 12:14:19 -0700952 else:
953 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700954 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700955
956 args = common.ParseOptions(argv, __doc__,
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800957 extra_opts="b:k:i:d:wne:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -0700958 extra_long_opts=["board_config=",
959 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700960 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700961 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700962 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700963 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700964 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700965 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800966 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900967 "no_signing",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700968 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700969 extra_option_handler=option_handler)
970
971 if len(args) != 2:
972 common.Usage(__doc__)
973 sys.exit(1)
974
Doug Zongker1c390a22009-05-14 19:06:36 -0700975 if OPTIONS.extra_script is not None:
976 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
977
Doug Zongkereef39442009-04-02 12:14:19 -0700978 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800979 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700980
Doug Zongkereef39442009-04-02 12:14:19 -0700981 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -0700982 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -0700983
984 # If this image was originally labelled with SELinux contexts, make sure we
985 # also apply the labels in our new image. During building, the "file_contexts"
986 # is in the out/ directory tree, but for repacking from target-files.zip it's
987 # in the root directory of the ramdisk.
988 if "selinux_fc" in OPTIONS.info_dict:
989 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
990 "file_contexts")
991
Doug Zongker37974732010-09-16 17:44:38 -0700992 if OPTIONS.verbose:
993 print "--- target info ---"
994 common.DumpInfoDict(OPTIONS.info_dict)
995
Doug Zongkereb0a78a2014-01-27 10:01:06 -0800996 # If the caller explicitly specified the device-specific extensions
997 # path via -s/--device_specific, use that. Otherwise, use
998 # META/releasetools.py if it is present in the target target_files.
999 # Otherwise, take the path of the file from 'tool_extensions' in the
1000 # info dict and look for that in the local filesystem, relative to
1001 # the current directory.
1002
Doug Zongker37974732010-09-16 17:44:38 -07001003 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001004 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1005 if os.path.exists(from_input):
1006 print "(using device-specific extensions from target_files)"
1007 OPTIONS.device_specific = from_input
1008 else:
1009 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1010
Doug Zongker37974732010-09-16 17:44:38 -07001011 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001012 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001013
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001014 if OPTIONS.no_signing:
1015 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1016 else:
1017 temp_zip_file = tempfile.NamedTemporaryFile()
1018 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1019 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001020
1021 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001022 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001023 if OPTIONS.package_key is None:
1024 OPTIONS.package_key = OPTIONS.info_dict.get(
1025 "default_system_dev_certificate",
1026 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001027 else:
1028 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001029 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001030 OPTIONS.target_info_dict = OPTIONS.info_dict
1031 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001032 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001033 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001034 "default_system_dev_certificate",
1035 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001036 if OPTIONS.verbose:
1037 print "--- source info ---"
1038 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001039 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001040
1041 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001042
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001043 if not OPTIONS.no_signing:
1044 SignOutput(temp_zip_file.name, args[1])
1045 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001046
1047 common.Cleanup()
1048
1049 print "done."
1050
1051
1052if __name__ == '__main__':
1053 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001054 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001055 main(sys.argv[1:])
1056 except common.ExternalError, e:
1057 print
1058 print " ERROR: %s" % (e,)
1059 print
1060 sys.exit(1)