blob: 652052dbbc49e15f1e0e82026d040dff4665f816 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile, produces an OTA package that installs
19that build. An incremental OTA is produced if -i is given, otherwise
20a full OTA is produced.
21
22Usage: ota_from_target_files [flags] input_target_files output_ota_package
23
24 -b (--board_config) <file>
Doug Zongkerfdd8e692009-08-03 17:27:48 -070025 Deprecated.
Doug Zongkereef39442009-04-02 12:14:19 -070026
Doug Zongkerafb32ea2011-09-22 10:28:04 -070027 -k (--package_key) <key> Key to use to sign the package (default is
28 the value of default_system_dev_certificate from the input
29 target-files's META/misc_info.txt, or
30 "build/target/product/security/testkey" if that value is not
31 specified).
32
33 For incremental OTAs, the default value is based on the source
34 target-file, not the target build.
Doug Zongkereef39442009-04-02 12:14:19 -070035
36 -i (--incremental_from) <file>
37 Generate an incremental OTA using the given target-files zip as
38 the starting build.
39
Doug Zongkerdbfaae52009-04-21 17:12:54 -070040 -w (--wipe_user_data)
41 Generate an OTA package that will wipe the user data partition
42 when installed.
43
Doug Zongker962069c2009-04-23 11:41:58 -070044 -n (--no_prereq)
45 Omit the timestamp prereq check normally included at the top of
46 the build scripts (used for developer OTA packages which
47 legitimately need to go back and forth).
48
Doug Zongker1c390a22009-05-14 19:06:36 -070049 -e (--extra_script) <file>
50 Insert the contents of file at the end of the update script.
51
Hristo Bojinovdafb0422010-08-26 14:35:16 -070052 -a (--aslr_mode) <on|off>
53 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050054
Doug Zongker9b23f2c2013-11-25 14:44:12 -080055 -2 (--two_step)
56 Generate a 'two-step' OTA package, where recovery is updated
57 first, so that any changes made to the system partition are done
58 using the new recovery (new kernel, etc.).
59
Doug Zongkereef39442009-04-02 12:14:19 -070060"""
61
62import sys
63
64if sys.hexversion < 0x02040000:
65 print >> sys.stderr, "Python 2.4 or newer is required."
66 sys.exit(1)
67
68import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070069import errno
Doug Zongkereef39442009-04-02 12:14:19 -070070import os
71import re
Doug Zongkereef39442009-04-02 12:14:19 -070072import subprocess
73import tempfile
74import time
75import zipfile
76
davidcad0bb92011-03-15 14:21:38 +000077try:
78 from hashlib import sha1 as sha1
79except ImportError:
80 from sha import sha as sha1
81
Doug Zongkereef39442009-04-02 12:14:19 -070082import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070083import edify_generator
Doug Zongkereef39442009-04-02 12:14:19 -070084
85OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -070086OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -070087OPTIONS.incremental_source = None
88OPTIONS.require_verbatim = set()
89OPTIONS.prohibit_verbatim = set(("system/build.prop",))
90OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070091OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070092OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070093OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070094OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070095OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -080096OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +090097OPTIONS.no_signing = False
Doug Zongkereef39442009-04-02 12:14:19 -070098
99def MostPopularKey(d, default):
100 """Given a dict, return the key corresponding to the largest
101 value. Returns 'default' if the dict is empty."""
102 x = [(v, k) for (k, v) in d.iteritems()]
103 if not x: return default
104 x.sort()
105 return x[-1][1]
106
107
108def IsSymlink(info):
109 """Return true if the zipfile.ZipInfo object passed in represents a
110 symlink."""
111 return (info.external_attr >> 16) == 0120777
112
Hristo Bojinov96be7202010-08-02 10:26:17 -0700113def IsRegular(info):
114 """Return true if the zipfile.ZipInfo object passed in represents a
115 symlink."""
116 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700117
Michael Runge4038aa82013-12-13 18:06:28 -0800118def ClosestFileMatch(src, tgtfiles, existing):
119 """Returns the closest file match between a source file and list
120 of potential matches. The exact filename match is preferred,
121 then the sha1 is searched for, and finally a file with the same
122 basename is evaluated. Rename support in the updater-binary is
123 required for the latter checks to be used."""
124
125 result = tgtfiles.get("path:" + src.name)
126 if result is not None:
127 return result
128
129 if not OPTIONS.target_info_dict.get("update_rename_support", False):
130 return None
131
132 if src.size < 1000:
133 return None
134
135 result = tgtfiles.get("sha1:" + src.sha1)
136 if result is not None and existing.get(result.name) is None:
137 return result
138 result = tgtfiles.get("file:" + src.name.split("/")[-1])
139 if result is not None and existing.get(result.name) is None:
140 return result
141 return None
142
Doug Zongkereef39442009-04-02 12:14:19 -0700143class Item:
144 """Items represent the metadata (user, group, mode) of files and
145 directories in the system image."""
146 ITEMS = {}
147 def __init__(self, name, dir=False):
148 self.name = name
149 self.uid = None
150 self.gid = None
151 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700152 self.selabel = None
153 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700154 self.dir = dir
155
156 if name:
157 self.parent = Item.Get(os.path.dirname(name), dir=True)
158 self.parent.children.append(self)
159 else:
160 self.parent = None
161 if dir:
162 self.children = []
163
164 def Dump(self, indent=0):
165 if self.uid is not None:
166 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
167 else:
168 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
169 if self.dir:
170 print "%s%s" % (" "*indent, self.descendants)
171 print "%s%s" % (" "*indent, self.best_subtree)
172 for i in self.children:
173 i.Dump(indent=indent+1)
174
175 @classmethod
176 def Get(cls, name, dir=False):
177 if name not in cls.ITEMS:
178 cls.ITEMS[name] = Item(name, dir=dir)
179 return cls.ITEMS[name]
180
181 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700182 def GetMetadata(cls, input_zip):
183
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700184 # The target_files contains a record of what the uid,
185 # gid, and mode are supposed to be.
186 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700187
188 for line in output.split("\n"):
189 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700190 columns = line.split()
191 name, uid, gid, mode = columns[:4]
192 selabel = None
193 capabilities = None
194
195 # After the first 4 columns, there are a series of key=value
196 # pairs. Extract out the fields we care about.
197 for element in columns[4:]:
198 key, value = element.split("=")
199 if key == "selabel":
200 selabel = value
201 if key == "capabilities":
202 capabilities = value
203
Doug Zongker283e2a12010-03-15 17:52:32 -0700204 i = cls.ITEMS.get(name, None)
205 if i is not None:
206 i.uid = int(uid)
207 i.gid = int(gid)
208 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700209 i.selabel = selabel
210 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700211 if i.dir:
212 i.children.sort(key=lambda i: i.name)
213
214 # set metadata for the files generated by this script.
215 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700216 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700217 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700218 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700219
220 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700221 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
222 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700223 set_perm to correctly chown/chmod all the files to their desired
224 values. Recursively calls itself for all descendants.
225
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700226 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700227 all descendants of this node. (dmode or fmode may be None.) Also
228 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700229 dmode, fmode, selabel, capabilities) tuple that will match the most
230 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700231 """
232
233 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700234 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700235 for i in self.children:
236 if i.dir:
237 for k, v in i.CountChildMetadata().iteritems():
238 d[k] = d.get(k, 0) + v
239 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700240 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700241 d[k] = d.get(k, 0) + 1
242
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700243 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
244 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700245
246 # First, find the (uid, gid) pair that matches the most
247 # descendants.
248 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700249 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700250 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
251 ug = MostPopularKey(ug, (0, 0))
252
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700253 # Now find the dmode, fmode, selabel, and capabilities that match
254 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700255 best_dmode = (0, 0755)
256 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 best_selabel = (0, None)
258 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700259 for k, count in d.iteritems():
260 if k[:2] != ug: continue
261 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
262 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700263 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
264 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
265 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700266
267 return d
268
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700269 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700270 """Append set_perm/set_perm_recursive commands to 'script' to
271 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700272 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700273
274 self.CountChildMetadata()
275
276 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700277 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700278 # item (and all its children) have already been set to. We only
279 # need to issue set_perm/set_perm_recursive commands if we're
280 # supposed to be something different.
281 if item.dir:
282 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700283 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700284 current = item.best_subtree
285
286 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700287 item.mode != current[2] or item.selabel != current[4] or \
288 item.capabilities != current[5]:
289 script.SetPermissions("/"+item.name, item.uid, item.gid,
290 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700291
292 for i in item.children:
293 recurse(i, current)
294 else:
295 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700296 item.mode != current[3] or item.selabel != current[4] or \
297 item.capabilities != current[5]:
298 script.SetPermissions("/"+item.name, item.uid, item.gid,
299 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700300
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700301 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700302
303
304def CopySystemFiles(input_zip, output_zip=None,
305 substitute=None):
306 """Copies files underneath system/ in the input zip to the output
307 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800308 list of symlinks. output_zip may be None, in which case the copy is
309 skipped (but the other side effects still happen). substitute is an
310 optional dict of {output filename: contents} to be output instead of
311 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700312 """
313
314 symlinks = []
315
316 for info in input_zip.infolist():
317 if info.filename.startswith("SYSTEM/"):
318 basefilename = info.filename[7:]
319 if IsSymlink(info):
320 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700321 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700322 else:
323 info2 = copy.copy(info)
324 fn = info2.filename = "system/" + basefilename
325 if substitute and fn in substitute and substitute[fn] is None:
326 continue
327 if output_zip is not None:
328 if substitute and fn in substitute:
329 data = substitute[fn]
330 else:
331 data = input_zip.read(info.filename)
332 output_zip.writestr(info2, data)
333 if fn.endswith("/"):
334 Item.Get(fn[:-1], dir=True)
335 else:
336 Item.Get(fn, dir=False)
337
338 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800339 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700340
341
Doug Zongkereef39442009-04-02 12:14:19 -0700342def SignOutput(temp_zip_name, output_zip_name):
343 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
344 pw = key_passwords[OPTIONS.package_key]
345
Doug Zongker951495f2009-08-14 12:44:19 -0700346 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
347 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700348
349
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700350def AppendAssertions(script, info_dict):
351 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700352 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700353
Doug Zongkereef39442009-04-02 12:14:19 -0700354
Doug Zongkerc9253822014-02-04 12:17:58 -0800355def HasRecoveryPatch(target_files_zip):
356 try:
357 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
358 return True
359 except KeyError:
360 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700361
362
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700363def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700364 # TODO: how to determine this? We don't know what version it will
365 # be installed on top of. For now, we expect the API just won't
366 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700367 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700368
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700369 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
370 OPTIONS.info_dict),
371 "pre-device": GetBuildProp("ro.product.device",
372 OPTIONS.info_dict),
373 "post-timestamp": GetBuildProp("ro.build.date.utc",
374 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700375 }
376
Doug Zongker05d3dea2009-06-22 11:32:31 -0700377 device_specific = common.DeviceSpecificParams(
378 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700379 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700380 output_zip=output_zip,
381 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700382 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700383 metadata=metadata,
384 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700385
Doug Zongkerc9253822014-02-04 12:17:58 -0800386 has_recovery_patch = HasRecoveryPatch(input_zip)
387
Doug Zongker962069c2009-04-23 11:41:58 -0700388 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700389 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700390 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
391 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700392
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700393 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700394 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800395
396 # Two-step package strategy (in chronological order, which is *not*
397 # the order in which the generated script has things):
398 #
399 # if stage is not "2/3" or "3/3":
400 # write recovery image to boot partition
401 # set stage to "2/3"
402 # reboot to boot partition and restart recovery
403 # else if stage is "2/3":
404 # write recovery image to recovery partition
405 # set stage to "3/3"
406 # reboot to recovery partition and restart recovery
407 # else:
408 # (stage must be "3/3")
409 # set stage to ""
410 # do normal full package installation:
411 # wipe and install system, boot image, etc.
412 # set up system to update recovery partition on first boot
413 # complete script normally (allow recovery to mark itself finished and reboot)
414
415 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
416 OPTIONS.input_tmp, "RECOVERY")
417 if OPTIONS.two_step:
418 if not OPTIONS.info_dict.get("multistage_support", None):
419 assert False, "two-step packages not supported by this build"
420 fs = OPTIONS.info_dict["fstab"]["/misc"]
421 assert fs.fs_type.upper() == "EMMC", \
422 "two-step packages only supported on devices with EMMC /misc partitions"
423 bcb_dev = {"bcb_dev": fs.device}
424 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
425 script.AppendExtra("""
426if get_stage("%(bcb_dev)s", "stage") == "2/3" then
427""" % bcb_dev)
428 script.WriteRawImage("/recovery", "recovery.img")
429 script.AppendExtra("""
430set_stage("%(bcb_dev)s", "3/3");
431reboot_now("%(bcb_dev)s", "recovery");
432else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
433""" % bcb_dev)
434
Doug Zongkere5ff5902012-01-17 10:55:37 -0800435 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700436
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700437 script.ShowProgress(0.5, 0)
Doug Zongkereef39442009-04-02 12:14:19 -0700438
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700439 if OPTIONS.wipe_user_data:
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700440 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700441
Kenny Rootf32dc712012-04-08 10:42:34 -0700442 if "selinux_fc" in OPTIONS.info_dict:
443 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500444
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700445 script.FormatPartition("/system")
446 script.Mount("/system")
Doug Zongkerc9253822014-02-04 12:17:58 -0800447 if not has_recovery_patch:
448 script.UnpackPackageDir("recovery", "/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700449 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700450
Doug Zongker1807e702012-02-28 12:21:08 -0800451 symlinks = CopySystemFiles(input_zip, output_zip)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700452 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700453
Doug Zongker55d93282011-01-25 17:03:34 -0800454 boot_img = common.GetBootableImage("boot.img", "boot.img",
455 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800456
457 if not has_recovery_patch:
458 def output_sink(fn, data):
459 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
460 Item.Get("system/" + fn, dir=False)
461
462 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
463 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700464
Doug Zongker283e2a12010-03-15 17:52:32 -0700465 Item.GetMetadata(input_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700466 Item.Get("system").SetPermissions(script)
467
Doug Zongker37974732010-09-16 17:44:38 -0700468 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700469 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700470 script.ShowProgress(0.2, 0)
471
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700472 script.ShowProgress(0.2, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700473 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700474
475 script.ShowProgress(0.1, 0)
476 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700477
Doug Zongker1c390a22009-05-14 19:06:36 -0700478 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700479 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700480
Doug Zongker14833602010-02-02 13:12:04 -0800481 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800482
483 if OPTIONS.two_step:
484 script.AppendExtra("""
485set_stage("%(bcb_dev)s", "");
486""" % bcb_dev)
487 script.AppendExtra("else\n")
488 script.WriteRawImage("/boot", "recovery.img")
489 script.AppendExtra("""
490set_stage("%(bcb_dev)s", "2/3");
491reboot_now("%(bcb_dev)s", "");
492endif;
493endif;
494""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700495 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700496 WriteMetadata(metadata, output_zip)
497
Stephen Smalley56882bf2012-02-09 13:36:21 -0500498def WritePolicyConfig(file_context, output_zip):
499 f = open(file_context, 'r');
500 basename = os.path.basename(file_context)
501 common.ZipWriteStr(output_zip, basename, f.read())
502
Doug Zongker2ea21062010-04-28 16:05:21 -0700503
504def WriteMetadata(metadata, output_zip):
505 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
506 "".join(["%s=%s\n" % kv
507 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700508
Doug Zongkereef39442009-04-02 12:14:19 -0700509def LoadSystemFiles(z):
510 """Load all the files from SYSTEM/... in a given target-files
511 ZipFile, and return a dict of {filename: File object}."""
512 out = {}
513 for info in z.infolist():
514 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700515 basefilename = info.filename[7:]
516 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700517 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700518 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800519 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700520
521
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700522def GetBuildProp(prop, info_dict):
523 """Return the fingerprint of the build of a given target-files info_dict."""
524 try:
525 return info_dict.get("build.prop", {})[prop]
526 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700527 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700528
Michael Runge4038aa82013-12-13 18:06:28 -0800529def AddToKnownPaths(filename, known_paths):
530 if filename[-1] == "/":
531 return
532 dirs = filename.split("/")[:-1]
533 while len(dirs) > 0:
534 path = "/".join(dirs)
535 if path in known_paths:
536 break;
537 known_paths.add(path)
538 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700539
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700540def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Doug Zongker37974732010-09-16 17:44:38 -0700541 source_version = OPTIONS.source_info_dict["recovery_api_version"]
542 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700543
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700544 if source_version == 0:
545 print ("WARNING: generating edify script for a source that "
546 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700547 script = edify_generator.EdifyGenerator(source_version,
548 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700549
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700550 metadata = {"pre-device": GetBuildProp("ro.product.device",
551 OPTIONS.source_info_dict),
552 "post-timestamp": GetBuildProp("ro.build.date.utc",
553 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700554 }
555
Doug Zongker05d3dea2009-06-22 11:32:31 -0700556 device_specific = common.DeviceSpecificParams(
557 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800558 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700559 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800560 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700561 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700562 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700563 metadata=metadata,
564 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700565
Doug Zongkereef39442009-04-02 12:14:19 -0700566 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800567 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700568 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800569 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700570
Doug Zongkerc9253822014-02-04 12:17:58 -0800571 target_has_recovery_patch = HasRecoveryPatch(target_zip)
572
Doug Zongkereef39442009-04-02 12:14:19 -0700573 verbatim_targets = []
574 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700575 diffs = []
Michael Runge4038aa82013-12-13 18:06:28 -0800576 renames = {}
577 known_paths = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700578 largest_source_size = 0
Michael Runge4038aa82013-12-13 18:06:28 -0800579
580 matching_file_cache = {}
581 for fn, sf in source_data.items():
582 assert fn == sf.name
583 matching_file_cache["path:" + fn] = sf
584 if fn in target_data.keys():
585 AddToKnownPaths(fn, known_paths)
586 # Only allow eligibility for filename/sha matching
587 # if there isn't a perfect path match.
588 if target_data.get(sf.name) is None:
589 matching_file_cache["file:" + fn.split("/")[-1]] = sf
590 matching_file_cache["sha:" + sf.sha1] = sf
591
Doug Zongkereef39442009-04-02 12:14:19 -0700592 for fn in sorted(target_data.keys()):
593 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700594 assert fn == tf.name
Michael Runge4038aa82013-12-13 18:06:28 -0800595 sf = ClosestFileMatch(tf, matching_file_cache, renames)
596 if sf is not None and sf.name != tf.name:
597 print "File has moved from " + sf.name + " to " + tf.name
598 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700599
600 if sf is None or fn in OPTIONS.require_verbatim:
601 # This file should be included verbatim
602 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700603 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700604 print "send", fn, "verbatim"
605 tf.AddToZip(output_zip)
606 verbatim_targets.append((fn, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800607 if fn in target_data.keys():
608 AddToKnownPaths(fn, known_paths)
Doug Zongkereef39442009-04-02 12:14:19 -0700609 elif tf.sha1 != sf.sha1:
610 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700611 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700612 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800613 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700614 pass
615
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700616 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700617
618 for diff in diffs:
619 tf, sf, d = diff.GetPatch()
Michael Runge4038aa82013-12-13 18:06:28 -0800620 path = "/".join(tf.name.split("/")[:-1])
621 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
622 path not in known_paths:
Doug Zongker761e6422009-09-25 10:45:39 -0700623 # patch is almost as big as the file; don't bother patching
Michael Runge4038aa82013-12-13 18:06:28 -0800624 # or a patch + rename cannot take place due to the target
625 # directory not existing
Doug Zongker761e6422009-09-25 10:45:39 -0700626 tf.AddToZip(output_zip)
627 verbatim_targets.append((tf.name, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800628 if sf.name in renames:
629 del renames[sf.name]
630 AddToKnownPaths(tf.name, known_paths)
Doug Zongker761e6422009-09-25 10:45:39 -0700631 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800632 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
633 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700634 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700635
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700636 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
637 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700638 metadata["pre-build"] = source_fp
639 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700640
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700641 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700642 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700643
Doug Zongker55d93282011-01-25 17:03:34 -0800644 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700645 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
646 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800647 target_boot = common.GetBootableImage(
648 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800649 updating_boot = (not OPTIONS.two_step and
650 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700651
Doug Zongker55d93282011-01-25 17:03:34 -0800652 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700653 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
654 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800655 target_recovery = common.GetBootableImage(
656 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700657 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700658
Doug Zongker881dd402009-09-20 14:03:55 -0700659 # Here's how we divide up the progress bar:
660 # 0.1 for verifying the start state (PatchCheck calls)
661 # 0.8 for applying patches (ApplyPatch calls)
662 # 0.1 for unpacking verbatim files, symlinking, and doing the
663 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700664
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700665 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700666 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700667
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800668 # Two-step incremental package strategy (in chronological order,
669 # which is *not* the order in which the generated script has
670 # things):
671 #
672 # if stage is not "2/3" or "3/3":
673 # do verification on current system
674 # write recovery image to boot partition
675 # set stage to "2/3"
676 # reboot to boot partition and restart recovery
677 # else if stage is "2/3":
678 # write recovery image to recovery partition
679 # set stage to "3/3"
680 # reboot to recovery partition and restart recovery
681 # else:
682 # (stage must be "3/3")
683 # perform update:
684 # patch system files, etc.
685 # force full install of new boot image
686 # set up system to update recovery partition on first boot
687 # complete script normally (allow recovery to mark itself finished and reboot)
688
689 if OPTIONS.two_step:
690 if not OPTIONS.info_dict.get("multistage_support", None):
691 assert False, "two-step packages not supported by this build"
692 fs = OPTIONS.info_dict["fstab"]["/misc"]
693 assert fs.fs_type.upper() == "EMMC", \
694 "two-step packages only supported on devices with EMMC /misc partitions"
695 bcb_dev = {"bcb_dev": fs.device}
696 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
697 script.AppendExtra("""
698if get_stage("%(bcb_dev)s", "stage") == "2/3" then
699""" % bcb_dev)
700 script.AppendExtra("sleep(20);\n");
701 script.WriteRawImage("/recovery", "recovery.img")
702 script.AppendExtra("""
703set_stage("%(bcb_dev)s", "3/3");
704reboot_now("%(bcb_dev)s", "recovery");
705else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
706""" % bcb_dev)
707
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700708 script.Print("Verifying current system...")
709
Doug Zongkere5ff5902012-01-17 10:55:37 -0800710 device_specific.IncrementalOTA_VerifyBegin()
711
Doug Zongker881dd402009-09-20 14:03:55 -0700712 script.ShowProgress(0.1, 0)
Michael Runge4038aa82013-12-13 18:06:28 -0800713 total_verify_size = float(sum([i[1].size for i in patch_list]) + 1)
Doug Zongker881dd402009-09-20 14:03:55 -0700714 if updating_boot:
715 total_verify_size += source_boot.size
716 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700717
Michael Runge4038aa82013-12-13 18:06:28 -0800718 for tf, sf, size, patch_sha in patch_list:
719 if tf.name != sf.name:
720 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
721 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700722 so_far += sf.size
723 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700724
Doug Zongker5da317e2009-06-02 13:38:17 -0700725 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700726 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700727 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700728 print "boot target: %d source: %d diff: %d" % (
729 target_boot.size, source_boot.size, len(d))
730
Doug Zongker048e7ca2009-06-15 14:31:53 -0700731 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700732
Doug Zongker96a57e72010-09-26 14:57:41 -0700733 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700734
735 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
736 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700737 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700738 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700739 so_far += source_boot.size
740 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700741
742 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700743 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800744
Doug Zongker05d3dea2009-06-22 11:32:31 -0700745 device_specific.IncrementalOTA_VerifyEnd()
746
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800747 if OPTIONS.two_step:
748 script.WriteRawImage("/boot", "recovery.img")
749 script.AppendExtra("""
750set_stage("%(bcb_dev)s", "2/3");
751reboot_now("%(bcb_dev)s", "");
752else
753""" % bcb_dev)
754
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700755 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700756
Doug Zongkere5ff5902012-01-17 10:55:37 -0800757 device_specific.IncrementalOTA_InstallBegin()
758
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800759 if OPTIONS.two_step:
760 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
761 script.WriteRawImage("/boot", "boot.img")
762 print "writing full boot image (forced by two-step mode)"
763
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700764 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700765 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700766 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700767
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700768 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700769 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
770 ["/"+i for i in sorted(source_data)
Michael Runge4038aa82013-12-13 18:06:28 -0800771 if i not in target_data and
772 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700773 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700774
Doug Zongker881dd402009-09-20 14:03:55 -0700775 script.ShowProgress(0.8, 0)
776 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
777 if updating_boot:
778 total_patch_size += target_boot.size
779 so_far = 0
780
781 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700782 deferred_patch_list = []
783 for item in patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800784 tf, sf, size, _ = item
Doug Zongkere92f15a2011-08-26 13:46:40 -0700785 if tf.name == "system/build.prop":
786 deferred_patch_list.append(item)
787 continue
Michael Runge4038aa82013-12-13 18:06:28 -0800788 if (sf.name != tf.name):
789 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
790 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Doug Zongker881dd402009-09-20 14:03:55 -0700791 so_far += tf.size
792 script.SetProgress(so_far / total_patch_size)
793
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800794 if not OPTIONS.two_step:
795 if updating_boot:
796 # Produce the boot image by applying a patch to the current
797 # contents of the boot partition, and write it back to the
798 # partition.
799 script.Print("Patching boot image...")
800 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
801 % (boot_type, boot_device,
802 source_boot.size, source_boot.sha1,
803 target_boot.size, target_boot.sha1),
804 "-",
805 target_boot.size, target_boot.sha1,
806 source_boot.sha1, "patch/boot.img.p")
807 so_far += target_boot.size
808 script.SetProgress(so_far / total_patch_size)
809 print "boot image changed; including."
810 else:
811 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -0700812
813 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -0700814 # Recovery is generated as a patch using both the boot image
815 # (which contains the same linux kernel as recovery) and the file
816 # /system/etc/recovery-resource.dat (which contains all the images
817 # used in the recovery UI) as sources. This lets us minimize the
818 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -0700819 #
Doug Zongkerb32161a2012-08-21 10:33:44 -0700820 # For older builds where recovery-resource.dat is not present, we
821 # use only the boot image as the source.
822
Doug Zongkerc9253822014-02-04 12:17:58 -0800823 if not target_has_recovery_patch:
824 def output_sink(fn, data):
825 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
826 Item.Get("system/" + fn, dir=False)
827
828 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
829 target_recovery, target_boot)
830 script.DeleteFiles(["/system/recovery-from-boot.p",
831 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -0700832 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -0700833 else:
834 print "recovery image unchanged; skipping."
835
Doug Zongker881dd402009-09-20 14:03:55 -0700836 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -0700837
Doug Zongker1807e702012-02-28 12:21:08 -0800838 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700839
840 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700841 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -0700842 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -0700843 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700844
845 # Note that this call will mess up the tree of Items, so make sure
846 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -0800847 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700848 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
849
850 # Delete all the symlinks in source that aren't in target. This
851 # needs to happen before verbatim files are unpacked, in case a
852 # symlink in the source is replaced by a real file in the target.
853 to_delete = []
854 for dest, link in source_symlinks:
855 if link not in target_symlinks_d:
856 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700857 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -0700858
859 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700860 script.Print("Unpacking new files...")
861 script.UnpackPackageDir("system", "/system")
862
Doug Zongkerc9253822014-02-04 12:17:58 -0800863 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -0800864 script.Print("Unpacking new recovery...")
865 script.UnpackPackageDir("recovery", "/system")
866
Michael Runge4038aa82013-12-13 18:06:28 -0800867 if len(renames) > 0:
868 script.Print("Renaming files...")
869
870 for src in renames:
871 print "Renaming " + src + " to " + renames[src].name
872 script.RenameFile(src, renames[src].name)
873
Doug Zongker05d3dea2009-06-22 11:32:31 -0700874 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -0700875
876 # Create all the symlinks that don't already exist, or point to
877 # somewhere different than what we want. Delete each symlink before
878 # creating it, since the 'symlink' command won't overwrite.
879 to_create = []
880 for dest, link in target_symlinks:
881 if link in source_symlinks_d:
882 if dest != source_symlinks_d[link]:
883 to_create.append((dest, link))
884 else:
885 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700886 script.DeleteFiles([i[1] for i in to_create])
887 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -0700888
889 # Now that the symlinks are created, we can set all the
890 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700891 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -0700892
Doug Zongker881dd402009-09-20 14:03:55 -0700893 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -0700894 device_specific.IncrementalOTA_InstallEnd()
895
Doug Zongker1c390a22009-05-14 19:06:36 -0700896 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -0700897 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700898
Doug Zongkere92f15a2011-08-26 13:46:40 -0700899 # Patch the build.prop file last, so if something fails but the
900 # device can still come up, it appears to be the old build and will
901 # get set the OTA package again to retry.
902 script.Print("Patching remaining system files...")
903 for item in deferred_patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800904 tf, sf, size, _ = item
905 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700906 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -0700907
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800908 if OPTIONS.two_step:
909 script.AppendExtra("""
910set_stage("%(bcb_dev)s", "");
911endif;
912endif;
913""" % bcb_dev)
914
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700915 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700916 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700917
918
919def main(argv):
920
921 def option_handler(o, a):
922 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700923 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -0700924 elif o in ("-k", "--package_key"):
925 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -0700926 elif o in ("-i", "--incremental_from"):
927 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700928 elif o in ("-w", "--wipe_user_data"):
929 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -0700930 elif o in ("-n", "--no_prereq"):
931 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -0700932 elif o in ("-e", "--extra_script"):
933 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700934 elif o in ("-a", "--aslr_mode"):
935 if a in ("on", "On", "true", "True", "yes", "Yes"):
936 OPTIONS.aslr_mode = True
937 else:
938 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -0700939 elif o in ("--worker_threads"):
940 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800941 elif o in ("-2", "--two_step"):
942 OPTIONS.two_step = True
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900943 elif o in ("--no_signing"):
944 OPTIONS.no_signing = True
Doug Zongkereef39442009-04-02 12:14:19 -0700945 else:
946 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700947 return True
Doug Zongkereef39442009-04-02 12:14:19 -0700948
949 args = common.ParseOptions(argv, __doc__,
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800950 extra_opts="b:k:i:d:wne:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -0700951 extra_long_opts=["board_config=",
952 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700953 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -0700954 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -0700955 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700956 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -0700957 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700958 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800959 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900960 "no_signing",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -0700961 ],
Doug Zongkereef39442009-04-02 12:14:19 -0700962 extra_option_handler=option_handler)
963
964 if len(args) != 2:
965 common.Usage(__doc__)
966 sys.exit(1)
967
Doug Zongker1c390a22009-05-14 19:06:36 -0700968 if OPTIONS.extra_script is not None:
969 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
970
Doug Zongkereef39442009-04-02 12:14:19 -0700971 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -0800972 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -0700973
Doug Zongkereef39442009-04-02 12:14:19 -0700974 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -0700975 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -0700976
977 # If this image was originally labelled with SELinux contexts, make sure we
978 # also apply the labels in our new image. During building, the "file_contexts"
979 # is in the out/ directory tree, but for repacking from target-files.zip it's
980 # in the root directory of the ramdisk.
981 if "selinux_fc" in OPTIONS.info_dict:
982 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
983 "file_contexts")
984
Doug Zongker37974732010-09-16 17:44:38 -0700985 if OPTIONS.verbose:
986 print "--- target info ---"
987 common.DumpInfoDict(OPTIONS.info_dict)
988
Doug Zongkereb0a78a2014-01-27 10:01:06 -0800989 # If the caller explicitly specified the device-specific extensions
990 # path via -s/--device_specific, use that. Otherwise, use
991 # META/releasetools.py if it is present in the target target_files.
992 # Otherwise, take the path of the file from 'tool_extensions' in the
993 # info dict and look for that in the local filesystem, relative to
994 # the current directory.
995
Doug Zongker37974732010-09-16 17:44:38 -0700996 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -0800997 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
998 if os.path.exists(from_input):
999 print "(using device-specific extensions from target_files)"
1000 OPTIONS.device_specific = from_input
1001 else:
1002 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1003
Doug Zongker37974732010-09-16 17:44:38 -07001004 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001005 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001006
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001007 if OPTIONS.no_signing:
1008 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1009 else:
1010 temp_zip_file = tempfile.NamedTemporaryFile()
1011 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1012 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001013
1014 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001015 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001016 if OPTIONS.package_key is None:
1017 OPTIONS.package_key = OPTIONS.info_dict.get(
1018 "default_system_dev_certificate",
1019 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001020 else:
1021 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001022 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001023 OPTIONS.target_info_dict = OPTIONS.info_dict
1024 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001025 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001026 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001027 "default_system_dev_certificate",
1028 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001029 if OPTIONS.verbose:
1030 print "--- source info ---"
1031 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001032 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001033
1034 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001035
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001036 if not OPTIONS.no_signing:
1037 SignOutput(temp_zip_file.name, args[1])
1038 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001039
1040 common.Cleanup()
1041
1042 print "done."
1043
1044
1045if __name__ == '__main__':
1046 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001047 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001048 main(sys.argv[1:])
1049 except common.ExternalError, e:
1050 print
1051 print " ERROR: %s" % (e,)
1052 print
1053 sys.exit(1)