blob: 6cccaaa70beab612ffe8edd8eda016d33d583a5f [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
Geremy Condra36bd3652014-02-06 19:45:10 -080085import build_image
Doug Zongkereef39442009-04-02 12:14:19 -070086
87OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -070088OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -070089OPTIONS.incremental_source = None
90OPTIONS.require_verbatim = set()
91OPTIONS.prohibit_verbatim = set(("system/build.prop",))
92OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -070093OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -070094OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -070095OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -070096OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -070097OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -080098OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +090099OPTIONS.no_signing = False
Doug Zongkereef39442009-04-02 12:14:19 -0700100
101def MostPopularKey(d, default):
102 """Given a dict, return the key corresponding to the largest
103 value. Returns 'default' if the dict is empty."""
104 x = [(v, k) for (k, v) in d.iteritems()]
105 if not x: return default
106 x.sort()
107 return x[-1][1]
108
109
110def IsSymlink(info):
111 """Return true if the zipfile.ZipInfo object passed in represents a
112 symlink."""
113 return (info.external_attr >> 16) == 0120777
114
Hristo Bojinov96be7202010-08-02 10:26:17 -0700115def IsRegular(info):
116 """Return true if the zipfile.ZipInfo object passed in represents a
117 symlink."""
118 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700119
Michael Runge4038aa82013-12-13 18:06:28 -0800120def ClosestFileMatch(src, tgtfiles, existing):
121 """Returns the closest file match between a source file and list
122 of potential matches. The exact filename match is preferred,
123 then the sha1 is searched for, and finally a file with the same
124 basename is evaluated. Rename support in the updater-binary is
125 required for the latter checks to be used."""
126
127 result = tgtfiles.get("path:" + src.name)
128 if result is not None:
129 return result
130
131 if not OPTIONS.target_info_dict.get("update_rename_support", False):
132 return None
133
134 if src.size < 1000:
135 return None
136
137 result = tgtfiles.get("sha1:" + src.sha1)
138 if result is not None and existing.get(result.name) is None:
139 return result
140 result = tgtfiles.get("file:" + src.name.split("/")[-1])
141 if result is not None and existing.get(result.name) is None:
142 return result
143 return None
144
Doug Zongkereef39442009-04-02 12:14:19 -0700145class Item:
146 """Items represent the metadata (user, group, mode) of files and
147 directories in the system image."""
148 ITEMS = {}
149 def __init__(self, name, dir=False):
150 self.name = name
151 self.uid = None
152 self.gid = None
153 self.mode = None
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700154 self.selabel = None
155 self.capabilities = None
Doug Zongkereef39442009-04-02 12:14:19 -0700156 self.dir = dir
157
158 if name:
159 self.parent = Item.Get(os.path.dirname(name), dir=True)
160 self.parent.children.append(self)
161 else:
162 self.parent = None
163 if dir:
164 self.children = []
165
166 def Dump(self, indent=0):
167 if self.uid is not None:
168 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
169 else:
170 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
171 if self.dir:
172 print "%s%s" % (" "*indent, self.descendants)
173 print "%s%s" % (" "*indent, self.best_subtree)
174 for i in self.children:
175 i.Dump(indent=indent+1)
176
177 @classmethod
178 def Get(cls, name, dir=False):
179 if name not in cls.ITEMS:
180 cls.ITEMS[name] = Item(name, dir=dir)
181 return cls.ITEMS[name]
182
183 @classmethod
Doug Zongker283e2a12010-03-15 17:52:32 -0700184 def GetMetadata(cls, input_zip):
185
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700186 # The target_files contains a record of what the uid,
187 # gid, and mode are supposed to be.
188 output = input_zip.read("META/filesystem_config.txt")
Doug Zongkereef39442009-04-02 12:14:19 -0700189
190 for line in output.split("\n"):
191 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700192 columns = line.split()
193 name, uid, gid, mode = columns[:4]
194 selabel = None
195 capabilities = None
196
197 # After the first 4 columns, there are a series of key=value
198 # pairs. Extract out the fields we care about.
199 for element in columns[4:]:
200 key, value = element.split("=")
201 if key == "selabel":
202 selabel = value
203 if key == "capabilities":
204 capabilities = value
205
Doug Zongker283e2a12010-03-15 17:52:32 -0700206 i = cls.ITEMS.get(name, None)
207 if i is not None:
208 i.uid = int(uid)
209 i.gid = int(gid)
210 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700211 i.selabel = selabel
212 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700213 if i.dir:
214 i.children.sort(key=lambda i: i.name)
215
216 # set metadata for the files generated by this script.
217 i = cls.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700218 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongker283e2a12010-03-15 17:52:32 -0700219 i = cls.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700220 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700221
222 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700223 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
224 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700225 set_perm to correctly chown/chmod all the files to their desired
226 values. Recursively calls itself for all descendants.
227
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700228 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700229 all descendants of this node. (dmode or fmode may be None.) Also
230 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700231 dmode, fmode, selabel, capabilities) tuple that will match the most
232 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700233 """
234
235 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700236 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700237 for i in self.children:
238 if i.dir:
239 for k, v in i.CountChildMetadata().iteritems():
240 d[k] = d.get(k, 0) + v
241 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700242 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700243 d[k] = d.get(k, 0) + 1
244
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700245 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
246 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700247
248 # First, find the (uid, gid) pair that matches the most
249 # descendants.
250 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700251 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700252 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
253 ug = MostPopularKey(ug, (0, 0))
254
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700255 # Now find the dmode, fmode, selabel, and capabilities that match
256 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700257 best_dmode = (0, 0755)
258 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700259 best_selabel = (0, None)
260 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700261 for k, count in d.iteritems():
262 if k[:2] != ug: continue
263 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
264 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700265 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
266 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
267 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700268
269 return d
270
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700271 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700272 """Append set_perm/set_perm_recursive commands to 'script' to
273 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700274 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700275
276 self.CountChildMetadata()
277
278 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700279 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700280 # item (and all its children) have already been set to. We only
281 # need to issue set_perm/set_perm_recursive commands if we're
282 # supposed to be something different.
283 if item.dir:
284 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700285 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700286 current = item.best_subtree
287
288 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700289 item.mode != current[2] or item.selabel != current[4] or \
290 item.capabilities != current[5]:
291 script.SetPermissions("/"+item.name, item.uid, item.gid,
292 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700293
294 for i in item.children:
295 recurse(i, current)
296 else:
297 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700298 item.mode != current[3] or item.selabel != current[4] or \
299 item.capabilities != current[5]:
300 script.SetPermissions("/"+item.name, item.uid, item.gid,
301 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700302
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700303 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700304
305
306def CopySystemFiles(input_zip, output_zip=None,
307 substitute=None):
308 """Copies files underneath system/ in the input zip to the output
309 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800310 list of symlinks. output_zip may be None, in which case the copy is
311 skipped (but the other side effects still happen). substitute is an
312 optional dict of {output filename: contents} to be output instead of
313 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700314 """
315
316 symlinks = []
317
318 for info in input_zip.infolist():
319 if info.filename.startswith("SYSTEM/"):
320 basefilename = info.filename[7:]
321 if IsSymlink(info):
322 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700323 "/system/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700324 else:
325 info2 = copy.copy(info)
326 fn = info2.filename = "system/" + basefilename
327 if substitute and fn in substitute and substitute[fn] is None:
328 continue
329 if output_zip is not None:
330 if substitute and fn in substitute:
331 data = substitute[fn]
332 else:
333 data = input_zip.read(info.filename)
334 output_zip.writestr(info2, data)
335 if fn.endswith("/"):
336 Item.Get(fn[:-1], dir=True)
337 else:
338 Item.Get(fn, dir=False)
339
340 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800341 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700342
343
Doug Zongkereef39442009-04-02 12:14:19 -0700344def SignOutput(temp_zip_name, output_zip_name):
345 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
346 pw = key_passwords[OPTIONS.package_key]
347
Doug Zongker951495f2009-08-14 12:44:19 -0700348 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
349 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700350
351
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700352def AppendAssertions(script, info_dict):
353 device = GetBuildProp("ro.product.device", info_dict)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700354 script.AssertDevice(device)
Doug Zongkereef39442009-04-02 12:14:19 -0700355
Doug Zongkereef39442009-04-02 12:14:19 -0700356
Doug Zongkerc9253822014-02-04 12:17:58 -0800357def HasRecoveryPatch(target_files_zip):
358 try:
359 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
360 return True
361 except KeyError:
362 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700363
364
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700365def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700366 # TODO: how to determine this? We don't know what version it will
367 # be installed on top of. For now, we expect the API just won't
368 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700369 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700370
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700371 metadata = {"post-build": GetBuildProp("ro.build.fingerprint",
372 OPTIONS.info_dict),
373 "pre-device": GetBuildProp("ro.product.device",
374 OPTIONS.info_dict),
375 "post-timestamp": GetBuildProp("ro.build.date.utc",
376 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700377 }
378
Doug Zongker05d3dea2009-06-22 11:32:31 -0700379 device_specific = common.DeviceSpecificParams(
380 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700381 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700382 output_zip=output_zip,
383 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700384 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700385 metadata=metadata,
386 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700387
Doug Zongkerc9253822014-02-04 12:17:58 -0800388 has_recovery_patch = HasRecoveryPatch(input_zip)
389
Doug Zongker962069c2009-04-23 11:41:58 -0700390 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700391 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700392 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
393 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700394
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700395 AppendAssertions(script, OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700396 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800397
398 # Two-step package strategy (in chronological order, which is *not*
399 # the order in which the generated script has things):
400 #
401 # if stage is not "2/3" or "3/3":
402 # write recovery image to boot partition
403 # set stage to "2/3"
404 # reboot to boot partition and restart recovery
405 # else if stage is "2/3":
406 # write recovery image to recovery partition
407 # set stage to "3/3"
408 # reboot to recovery partition and restart recovery
409 # else:
410 # (stage must be "3/3")
411 # set stage to ""
412 # do normal full package installation:
413 # wipe and install system, boot image, etc.
414 # set up system to update recovery partition on first boot
415 # complete script normally (allow recovery to mark itself finished and reboot)
416
417 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
418 OPTIONS.input_tmp, "RECOVERY")
419 if OPTIONS.two_step:
420 if not OPTIONS.info_dict.get("multistage_support", None):
421 assert False, "two-step packages not supported by this build"
422 fs = OPTIONS.info_dict["fstab"]["/misc"]
423 assert fs.fs_type.upper() == "EMMC", \
424 "two-step packages only supported on devices with EMMC /misc partitions"
425 bcb_dev = {"bcb_dev": fs.device}
426 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
427 script.AppendExtra("""
428if get_stage("%(bcb_dev)s", "stage") == "2/3" then
429""" % bcb_dev)
430 script.WriteRawImage("/recovery", "recovery.img")
431 script.AppendExtra("""
432set_stage("%(bcb_dev)s", "3/3");
433reboot_now("%(bcb_dev)s", "recovery");
434else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
435""" % bcb_dev)
436
Doug Zongkere5ff5902012-01-17 10:55:37 -0800437 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700438
Doug Zongker01ce19c2014-02-04 13:48:15 -0800439 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700440
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700441 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800442 system_progress -= 0.1
443 script.ShowProgress(0.1, 10)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700444 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700445
Kenny Rootf32dc712012-04-08 10:42:34 -0700446 if "selinux_fc" in OPTIONS.info_dict:
447 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500448
Doug Zongker01ce19c2014-02-04 13:48:15 -0800449 script.ShowProgress(system_progress, 30)
450 if has_recovery_patch:
451 img_from_target_files.AddSystem(output_zip, sparse=False)
452 script.WriteRawImage("/system", "system.img")
453 else:
454 script.FormatPartition("/system")
455 script.Mount("/system")
456 if not has_recovery_patch:
457 script.UnpackPackageDir("recovery", "/system")
458 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700459
Doug Zongker01ce19c2014-02-04 13:48:15 -0800460 symlinks = CopySystemFiles(input_zip, output_zip)
461 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700462
Doug Zongker55d93282011-01-25 17:03:34 -0800463 boot_img = common.GetBootableImage("boot.img", "boot.img",
464 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800465
466 if not has_recovery_patch:
467 def output_sink(fn, data):
468 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
469 Item.Get("system/" + fn, dir=False)
470
471 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
472 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700473
Doug Zongker01ce19c2014-02-04 13:48:15 -0800474 Item.GetMetadata(input_zip)
475 Item.Get("system").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700476
Doug Zongker37974732010-09-16 17:44:38 -0700477 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700478 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700479
Doug Zongker01ce19c2014-02-04 13:48:15 -0800480 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700481 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700482
Doug Zongker01ce19c2014-02-04 13:48:15 -0800483 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700484 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700485
Doug Zongker1c390a22009-05-14 19:06:36 -0700486 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700487 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700488
Doug Zongker14833602010-02-02 13:12:04 -0800489 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800490
491 if OPTIONS.two_step:
492 script.AppendExtra("""
493set_stage("%(bcb_dev)s", "");
494""" % bcb_dev)
495 script.AppendExtra("else\n")
496 script.WriteRawImage("/boot", "recovery.img")
497 script.AppendExtra("""
498set_stage("%(bcb_dev)s", "2/3");
499reboot_now("%(bcb_dev)s", "");
500endif;
501endif;
502""" % bcb_dev)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700503 script.AddToZip(input_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -0700504 WriteMetadata(metadata, output_zip)
505
Stephen Smalley56882bf2012-02-09 13:36:21 -0500506def WritePolicyConfig(file_context, output_zip):
507 f = open(file_context, 'r');
508 basename = os.path.basename(file_context)
509 common.ZipWriteStr(output_zip, basename, f.read())
510
Doug Zongker2ea21062010-04-28 16:05:21 -0700511
512def WriteMetadata(metadata, output_zip):
513 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
514 "".join(["%s=%s\n" % kv
515 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700516
Doug Zongkereef39442009-04-02 12:14:19 -0700517def LoadSystemFiles(z):
518 """Load all the files from SYSTEM/... in a given target-files
519 ZipFile, and return a dict of {filename: File object}."""
520 out = {}
521 for info in z.infolist():
522 if info.filename.startswith("SYSTEM/") and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700523 basefilename = info.filename[7:]
524 fn = "system/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700525 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700526 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800527 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700528
529
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700530def GetBuildProp(prop, info_dict):
531 """Return the fingerprint of the build of a given target-files info_dict."""
532 try:
533 return info_dict.get("build.prop", {})[prop]
534 except KeyError:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700535 raise common.ExternalError("couldn't find %s in build.prop" % (property,))
Doug Zongkereef39442009-04-02 12:14:19 -0700536
Michael Runge4038aa82013-12-13 18:06:28 -0800537def AddToKnownPaths(filename, known_paths):
538 if filename[-1] == "/":
539 return
540 dirs = filename.split("/")[:-1]
541 while len(dirs) > 0:
542 path = "/".join(dirs)
543 if path in known_paths:
544 break;
545 known_paths.add(path)
546 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700547
Geremy Condra36bd3652014-02-06 19:45:10 -0800548def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
549 source_version = OPTIONS.source_info_dict["recovery_api_version"]
550 target_version = OPTIONS.target_info_dict["recovery_api_version"]
551
552 if source_version == 0:
553 print ("WARNING: generating edify script for a source that "
554 "can't install it.")
555 script = edify_generator.EdifyGenerator(source_version,
556 OPTIONS.target_info_dict)
557
558 metadata = {"pre-device": GetBuildProp("ro.product.device",
559 OPTIONS.source_info_dict),
560 "post-timestamp": GetBuildProp("ro.build.date.utc",
561 OPTIONS.target_info_dict),
562 }
563
564 device_specific = common.DeviceSpecificParams(
565 source_zip=source_zip,
566 source_version=source_version,
567 target_zip=target_zip,
568 target_version=target_version,
569 output_zip=output_zip,
570 script=script,
571 metadata=metadata,
572 info_dict=OPTIONS.info_dict)
573
574 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
575 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
576 metadata["pre-build"] = source_fp
577 metadata["post-build"] = target_fp
578
579 source_boot = common.GetBootableImage(
580 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
581 OPTIONS.source_info_dict)
582 target_boot = common.GetBootableImage(
583 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
584 updating_boot = (not OPTIONS.two_step and
585 (source_boot.data != target_boot.data))
586
587 source_recovery = common.GetBootableImage(
588 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
589 OPTIONS.source_info_dict)
590 target_recovery = common.GetBootableImage(
591 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
592 updating_recovery = (source_recovery.data != target_recovery.data)
593
594 with tempfile.NamedTemporaryFile() as src_file:
595 with tempfile.NamedTemporaryFile() as tgt_file:
596 print "building source system image..."
597 src_file = tempfile.NamedTemporaryFile()
598 src_data = img_from_target_files.BuildSystem(
599 OPTIONS.source_tmp, OPTIONS.source_info_dict, sparse=False)
600 src_sys_sha1 = sha1(src_data).hexdigest()
601 print "source system sha1:", src_sys_sha1
602 src_file.write(src_data)
603 src_data = None
604
605 print "building target system image..."
606 tgt_file = tempfile.NamedTemporaryFile()
607 tgt_data = img_from_target_files.BuildSystem(
608 OPTIONS.target_tmp, OPTIONS.target_info_dict, sparse=False)
609 tgt_sys_sha1 = sha1(tgt_data).hexdigest()
610 print "target system sha1:", tgt_sys_sha1
611 tgt_sys_len = len(tgt_data)
612 tgt_file.write(tgt_data)
613 tgt_data = None
614
615 system_type, system_device = common.GetTypeAndDevice("/system", OPTIONS.info_dict)
616 system_patch = common.MakeSystemPatch(src_file, tgt_file)
617 system_patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
618
619 AppendAssertions(script, OPTIONS.target_info_dict)
620 device_specific.IncrementalOTA_Assertions()
621
622 # Two-step incremental package strategy (in chronological order,
623 # which is *not* the order in which the generated script has
624 # things):
625 #
626 # if stage is not "2/3" or "3/3":
627 # do verification on current system
628 # write recovery image to boot partition
629 # set stage to "2/3"
630 # reboot to boot partition and restart recovery
631 # else if stage is "2/3":
632 # write recovery image to recovery partition
633 # set stage to "3/3"
634 # reboot to recovery partition and restart recovery
635 # else:
636 # (stage must be "3/3")
637 # perform update:
638 # patch system files, etc.
639 # force full install of new boot image
640 # set up system to update recovery partition on first boot
641 # complete script normally (allow recovery to mark itself finished and reboot)
642
643 if OPTIONS.two_step:
644 if not OPTIONS.info_dict.get("multistage_support", None):
645 assert False, "two-step packages not supported by this build"
646 fs = OPTIONS.info_dict["fstab"]["/misc"]
647 assert fs.fs_type.upper() == "EMMC", \
648 "two-step packages only supported on devices with EMMC /misc partitions"
649 bcb_dev = {"bcb_dev": fs.device}
650 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
651 script.AppendExtra("""
652if get_stage("%(bcb_dev)s", "stage") == "2/3" then
653""" % bcb_dev)
654 script.AppendExtra("sleep(20);\n");
655 script.WriteRawImage("/recovery", "recovery.img")
656 script.AppendExtra("""
657set_stage("%(bcb_dev)s", "3/3");
658reboot_now("%(bcb_dev)s", "recovery");
659else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
660""" % bcb_dev)
661
662 script.Print("Verifying current system...")
663
664 device_specific.IncrementalOTA_VerifyBegin()
665
666 script.AssertRecoveryFingerprint(source_fp, target_fp)
667
668 if updating_boot:
669 total_verify_size += OPTIONS.info_dict["boot_size"]
670 d = common.Difference(target_boot, source_boot)
671 _, _, d = d.ComputePatch()
672 print "boot target: %d source: %d diff: %d" % (
673 target_boot.size, source_boot.size, len(d))
674
675 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
676
677 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
678
679 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
680 (boot_type, boot_device,
681 source_boot.size, source_boot.sha1,
682 target_boot.size, target_boot.sha1))
683
684 device_specific.IncrementalOTA_VerifyEnd()
685
686 if OPTIONS.two_step:
687 script.WriteRawImage("/boot", "recovery.img")
688 script.AppendExtra("""
689set_stage("%(bcb_dev)s", "2/3");
690reboot_now("%(bcb_dev)s", "");
691else
692""" % bcb_dev)
693
694 script.Comment("---- start making changes here ----")
695
696 device_specific.IncrementalOTA_InstallBegin()
697
698 if OPTIONS.wipe_user_data:
699 script.Print("Erasing user data...")
700 script.FormatPartition("/data")
701
702 script.Print("Patching system image...")
703 script.Syspatch(system_device,
704 OPTIONS.info_dict["system_size"],
705 tgt_sys_sha1,
706 src_sys_sha1,
707 system_patch.name)
708
709 if OPTIONS.two_step:
710 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
711 script.WriteRawImage("/boot", "boot.img")
712 print "writing full boot image (forced by two-step mode)"
713
714 if not OPTIONS.two_step:
715 if updating_boot:
716 # Produce the boot image by applying a patch to the current
717 # contents of the boot partition, and write it back to the
718 # partition.
719 script.Print("Patching boot image...")
720 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
721 % (boot_type, boot_device,
722 source_boot.size, source_boot.sha1,
723 target_boot.size, target_boot.sha1),
724 "-",
725 target_boot.size, target_boot.sha1,
726 source_boot.sha1, "patch/boot.img.p")
727 print "boot image changed; including."
728 else:
729 print "boot image unchanged; skipping."
730
731 # Do device-specific installation (eg, write radio image).
732 device_specific.IncrementalOTA_InstallEnd()
733
734 if OPTIONS.extra_script is not None:
735 script.AppendExtra(OPTIONS.extra_script)
736
737 if OPTIONS.two_step:
738 script.AppendExtra("""
739set_stage("%(bcb_dev)s", "");
740endif;
741endif;
742""" % bcb_dev)
743
744 script.SetProgress(1)
745 script.AddToZip(target_zip, output_zip)
746 WriteMetadata(metadata, output_zip)
747
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700748def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -0800749 target_has_recovery_patch = HasRecoveryPatch(target_zip)
750 source_has_recovery_patch = HasRecoveryPatch(source_zip)
751
752 if target_has_recovery_patch and source_has_recovery_patch:
753 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
754
Doug Zongker37974732010-09-16 17:44:38 -0700755 source_version = OPTIONS.source_info_dict["recovery_api_version"]
756 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700757
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700758 if source_version == 0:
759 print ("WARNING: generating edify script for a source that "
760 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700761 script = edify_generator.EdifyGenerator(source_version,
762 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700763
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700764 metadata = {"pre-device": GetBuildProp("ro.product.device",
765 OPTIONS.source_info_dict),
766 "post-timestamp": GetBuildProp("ro.build.date.utc",
767 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700768 }
769
Doug Zongker05d3dea2009-06-22 11:32:31 -0700770 device_specific = common.DeviceSpecificParams(
771 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800772 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700773 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -0800774 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -0700775 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -0700776 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -0700777 metadata=metadata,
778 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700779
Doug Zongkereef39442009-04-02 12:14:19 -0700780 print "Loading target..."
Doug Zongker1807e702012-02-28 12:21:08 -0800781 target_data = LoadSystemFiles(target_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700782 print "Loading source..."
Doug Zongker1807e702012-02-28 12:21:08 -0800783 source_data = LoadSystemFiles(source_zip)
Doug Zongkereef39442009-04-02 12:14:19 -0700784
785 verbatim_targets = []
786 patch_list = []
Doug Zongker761e6422009-09-25 10:45:39 -0700787 diffs = []
Michael Runge4038aa82013-12-13 18:06:28 -0800788 renames = {}
789 known_paths = set()
Doug Zongkereef39442009-04-02 12:14:19 -0700790 largest_source_size = 0
Michael Runge4038aa82013-12-13 18:06:28 -0800791
792 matching_file_cache = {}
793 for fn, sf in source_data.items():
794 assert fn == sf.name
795 matching_file_cache["path:" + fn] = sf
796 if fn in target_data.keys():
797 AddToKnownPaths(fn, known_paths)
798 # Only allow eligibility for filename/sha matching
799 # if there isn't a perfect path match.
800 if target_data.get(sf.name) is None:
801 matching_file_cache["file:" + fn.split("/")[-1]] = sf
802 matching_file_cache["sha:" + sf.sha1] = sf
803
Doug Zongkereef39442009-04-02 12:14:19 -0700804 for fn in sorted(target_data.keys()):
805 tf = target_data[fn]
Doug Zongker761e6422009-09-25 10:45:39 -0700806 assert fn == tf.name
Michael Runge4038aa82013-12-13 18:06:28 -0800807 sf = ClosestFileMatch(tf, matching_file_cache, renames)
808 if sf is not None and sf.name != tf.name:
809 print "File has moved from " + sf.name + " to " + tf.name
810 renames[sf.name] = tf
Doug Zongkereef39442009-04-02 12:14:19 -0700811
812 if sf is None or fn in OPTIONS.require_verbatim:
813 # This file should be included verbatim
814 if fn in OPTIONS.prohibit_verbatim:
Doug Zongker9fc74c72009-06-23 16:27:38 -0700815 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
Doug Zongkereef39442009-04-02 12:14:19 -0700816 print "send", fn, "verbatim"
817 tf.AddToZip(output_zip)
818 verbatim_targets.append((fn, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800819 if fn in target_data.keys():
820 AddToKnownPaths(fn, known_paths)
Doug Zongkereef39442009-04-02 12:14:19 -0700821 elif tf.sha1 != sf.sha1:
822 # File is different; consider sending as a patch
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700823 diffs.append(common.Difference(tf, sf))
Doug Zongkereef39442009-04-02 12:14:19 -0700824 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800825 # Target file data identical to source (may still be renamed)
Doug Zongkereef39442009-04-02 12:14:19 -0700826 pass
827
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700828 common.ComputeDifferences(diffs)
Doug Zongker761e6422009-09-25 10:45:39 -0700829
830 for diff in diffs:
831 tf, sf, d = diff.GetPatch()
Michael Runge4038aa82013-12-13 18:06:28 -0800832 path = "/".join(tf.name.split("/")[:-1])
833 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
834 path not in known_paths:
Doug Zongker761e6422009-09-25 10:45:39 -0700835 # patch is almost as big as the file; don't bother patching
Michael Runge4038aa82013-12-13 18:06:28 -0800836 # or a patch + rename cannot take place due to the target
837 # directory not existing
Doug Zongker761e6422009-09-25 10:45:39 -0700838 tf.AddToZip(output_zip)
839 verbatim_targets.append((tf.name, tf.size))
Michael Runge4038aa82013-12-13 18:06:28 -0800840 if sf.name in renames:
841 del renames[sf.name]
842 AddToKnownPaths(tf.name, known_paths)
Doug Zongker761e6422009-09-25 10:45:39 -0700843 else:
Michael Runge4038aa82013-12-13 18:06:28 -0800844 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
845 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
Doug Zongker761e6422009-09-25 10:45:39 -0700846 largest_source_size = max(largest_source_size, sf.size)
Doug Zongkereef39442009-04-02 12:14:19 -0700847
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700848 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
849 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
Doug Zongker2ea21062010-04-28 16:05:21 -0700850 metadata["pre-build"] = source_fp
851 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -0700852
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700853 script.Mount("/system")
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700854 script.AssertSomeFingerprint(source_fp, target_fp)
Doug Zongkereef39442009-04-02 12:14:19 -0700855
Doug Zongker55d93282011-01-25 17:03:34 -0800856 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700857 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
858 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800859 target_boot = common.GetBootableImage(
860 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800861 updating_boot = (not OPTIONS.two_step and
862 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -0700863
Doug Zongker55d93282011-01-25 17:03:34 -0800864 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -0700865 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
866 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -0800867 target_recovery = common.GetBootableImage(
868 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -0700869 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -0700870
Doug Zongker881dd402009-09-20 14:03:55 -0700871 # Here's how we divide up the progress bar:
872 # 0.1 for verifying the start state (PatchCheck calls)
873 # 0.8 for applying patches (ApplyPatch calls)
874 # 0.1 for unpacking verbatim files, symlinking, and doing the
875 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -0700876
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700877 AppendAssertions(script, OPTIONS.target_info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700878 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -0700879
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800880 # Two-step incremental package strategy (in chronological order,
881 # which is *not* the order in which the generated script has
882 # things):
883 #
884 # if stage is not "2/3" or "3/3":
885 # do verification on current system
886 # write recovery image to boot partition
887 # set stage to "2/3"
888 # reboot to boot partition and restart recovery
889 # else if stage is "2/3":
890 # write recovery image to recovery partition
891 # set stage to "3/3"
892 # reboot to recovery partition and restart recovery
893 # else:
894 # (stage must be "3/3")
895 # perform update:
896 # patch system files, etc.
897 # force full install of new boot image
898 # set up system to update recovery partition on first boot
899 # complete script normally (allow recovery to mark itself finished and reboot)
900
901 if OPTIONS.two_step:
902 if not OPTIONS.info_dict.get("multistage_support", None):
903 assert False, "two-step packages not supported by this build"
904 fs = OPTIONS.info_dict["fstab"]["/misc"]
905 assert fs.fs_type.upper() == "EMMC", \
906 "two-step packages only supported on devices with EMMC /misc partitions"
907 bcb_dev = {"bcb_dev": fs.device}
908 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
909 script.AppendExtra("""
910if get_stage("%(bcb_dev)s", "stage") == "2/3" then
911""" % bcb_dev)
912 script.AppendExtra("sleep(20);\n");
913 script.WriteRawImage("/recovery", "recovery.img")
914 script.AppendExtra("""
915set_stage("%(bcb_dev)s", "3/3");
916reboot_now("%(bcb_dev)s", "recovery");
917else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
918""" % bcb_dev)
919
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700920 script.Print("Verifying current system...")
921
Doug Zongkere5ff5902012-01-17 10:55:37 -0800922 device_specific.IncrementalOTA_VerifyBegin()
923
Doug Zongker881dd402009-09-20 14:03:55 -0700924 script.ShowProgress(0.1, 0)
Michael Runge4038aa82013-12-13 18:06:28 -0800925 total_verify_size = float(sum([i[1].size for i in patch_list]) + 1)
Doug Zongker881dd402009-09-20 14:03:55 -0700926 if updating_boot:
927 total_verify_size += source_boot.size
928 so_far = 0
Doug Zongkereef39442009-04-02 12:14:19 -0700929
Michael Runge4038aa82013-12-13 18:06:28 -0800930 for tf, sf, size, patch_sha in patch_list:
931 if tf.name != sf.name:
932 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
933 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
Doug Zongker881dd402009-09-20 14:03:55 -0700934 so_far += sf.size
935 script.SetProgress(so_far / total_verify_size)
Doug Zongkereef39442009-04-02 12:14:19 -0700936
Doug Zongker5da317e2009-06-02 13:38:17 -0700937 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700938 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -0700939 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -0700940 print "boot target: %d source: %d diff: %d" % (
941 target_boot.size, source_boot.size, len(d))
942
Doug Zongker048e7ca2009-06-15 14:31:53 -0700943 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -0700944
Doug Zongker96a57e72010-09-26 14:57:41 -0700945 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -0700946
947 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
948 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -0700949 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700950 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -0700951 so_far += source_boot.size
952 script.SetProgress(so_far / total_verify_size)
Doug Zongker5da317e2009-06-02 13:38:17 -0700953
954 if patch_list or updating_recovery or updating_boot:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700955 script.CacheFreeSpaceCheck(largest_source_size)
Doug Zongker5a482092010-02-17 16:09:18 -0800956
Doug Zongker05d3dea2009-06-22 11:32:31 -0700957 device_specific.IncrementalOTA_VerifyEnd()
958
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800959 if OPTIONS.two_step:
960 script.WriteRawImage("/boot", "recovery.img")
961 script.AppendExtra("""
962set_stage("%(bcb_dev)s", "2/3");
963reboot_now("%(bcb_dev)s", "");
964else
965""" % bcb_dev)
966
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700967 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -0700968
Doug Zongkere5ff5902012-01-17 10:55:37 -0800969 device_specific.IncrementalOTA_InstallBegin()
970
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800971 if OPTIONS.two_step:
972 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
973 script.WriteRawImage("/boot", "boot.img")
974 print "writing full boot image (forced by two-step mode)"
975
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700976 if OPTIONS.wipe_user_data:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700977 script.Print("Erasing user data...")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700978 script.FormatPartition("/data")
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700979
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700980 script.Print("Removing unneeded files...")
Doug Zongker0f3298a2009-06-30 08:16:58 -0700981 script.DeleteFiles(["/"+i[0] for i in verbatim_targets] +
982 ["/"+i for i in sorted(source_data)
Michael Runge4038aa82013-12-13 18:06:28 -0800983 if i not in target_data and
984 i not in renames] +
Doug Zongker3b949f02009-08-24 10:24:32 -0700985 ["/system/recovery.img"])
Doug Zongkereef39442009-04-02 12:14:19 -0700986
Doug Zongker881dd402009-09-20 14:03:55 -0700987 script.ShowProgress(0.8, 0)
988 total_patch_size = float(sum([i[1].size for i in patch_list]) + 1)
989 if updating_boot:
990 total_patch_size += target_boot.size
991 so_far = 0
992
993 script.Print("Patching system files...")
Doug Zongkere92f15a2011-08-26 13:46:40 -0700994 deferred_patch_list = []
995 for item in patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -0800996 tf, sf, size, _ = item
Doug Zongkere92f15a2011-08-26 13:46:40 -0700997 if tf.name == "system/build.prop":
998 deferred_patch_list.append(item)
999 continue
Michael Runge4038aa82013-12-13 18:06:28 -08001000 if (sf.name != tf.name):
1001 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1002 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Doug Zongker881dd402009-09-20 14:03:55 -07001003 so_far += tf.size
1004 script.SetProgress(so_far / total_patch_size)
1005
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001006 if not OPTIONS.two_step:
1007 if updating_boot:
1008 # Produce the boot image by applying a patch to the current
1009 # contents of the boot partition, and write it back to the
1010 # partition.
1011 script.Print("Patching boot image...")
1012 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1013 % (boot_type, boot_device,
1014 source_boot.size, source_boot.sha1,
1015 target_boot.size, target_boot.sha1),
1016 "-",
1017 target_boot.size, target_boot.sha1,
1018 source_boot.sha1, "patch/boot.img.p")
1019 so_far += target_boot.size
1020 script.SetProgress(so_far / total_patch_size)
1021 print "boot image changed; including."
1022 else:
1023 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001024
1025 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001026 # Recovery is generated as a patch using both the boot image
1027 # (which contains the same linux kernel as recovery) and the file
1028 # /system/etc/recovery-resource.dat (which contains all the images
1029 # used in the recovery UI) as sources. This lets us minimize the
1030 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001031 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001032 # For older builds where recovery-resource.dat is not present, we
1033 # use only the boot image as the source.
1034
Doug Zongkerc9253822014-02-04 12:17:58 -08001035 if not target_has_recovery_patch:
1036 def output_sink(fn, data):
1037 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
1038 Item.Get("system/" + fn, dir=False)
1039
1040 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1041 target_recovery, target_boot)
1042 script.DeleteFiles(["/system/recovery-from-boot.p",
1043 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001044 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001045 else:
1046 print "recovery image unchanged; skipping."
1047
Doug Zongker881dd402009-09-20 14:03:55 -07001048 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001049
Doug Zongker1807e702012-02-28 12:21:08 -08001050 target_symlinks = CopySystemFiles(target_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -07001051
1052 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001053 temp_script = script.MakeTemporary()
Doug Zongker283e2a12010-03-15 17:52:32 -07001054 Item.GetMetadata(target_zip)
Doug Zongker73ef8252009-07-23 15:12:53 -07001055 Item.Get("system").SetPermissions(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001056
1057 # Note that this call will mess up the tree of Items, so make sure
1058 # we're done with it.
Doug Zongker1807e702012-02-28 12:21:08 -08001059 source_symlinks = CopySystemFiles(source_zip, None)
Doug Zongkereef39442009-04-02 12:14:19 -07001060 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1061
1062 # Delete all the symlinks in source that aren't in target. This
1063 # needs to happen before verbatim files are unpacked, in case a
1064 # symlink in the source is replaced by a real file in the target.
1065 to_delete = []
1066 for dest, link in source_symlinks:
1067 if link not in target_symlinks_d:
1068 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001069 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001070
1071 if verbatim_targets:
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001072 script.Print("Unpacking new files...")
1073 script.UnpackPackageDir("system", "/system")
1074
Doug Zongkerc9253822014-02-04 12:17:58 -08001075 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001076 script.Print("Unpacking new recovery...")
1077 script.UnpackPackageDir("recovery", "/system")
1078
Michael Runge4038aa82013-12-13 18:06:28 -08001079 if len(renames) > 0:
1080 script.Print("Renaming files...")
1081
1082 for src in renames:
1083 print "Renaming " + src + " to " + renames[src].name
1084 script.RenameFile(src, renames[src].name)
1085
Doug Zongker05d3dea2009-06-22 11:32:31 -07001086 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001087
1088 # Create all the symlinks that don't already exist, or point to
1089 # somewhere different than what we want. Delete each symlink before
1090 # creating it, since the 'symlink' command won't overwrite.
1091 to_create = []
1092 for dest, link in target_symlinks:
1093 if link in source_symlinks_d:
1094 if dest != source_symlinks_d[link]:
1095 to_create.append((dest, link))
1096 else:
1097 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001098 script.DeleteFiles([i[1] for i in to_create])
1099 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001100
1101 # Now that the symlinks are created, we can set all the
1102 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001103 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001104
Doug Zongker881dd402009-09-20 14:03:55 -07001105 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001106 device_specific.IncrementalOTA_InstallEnd()
1107
Doug Zongker1c390a22009-05-14 19:06:36 -07001108 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001109 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001110
Doug Zongkere92f15a2011-08-26 13:46:40 -07001111 # Patch the build.prop file last, so if something fails but the
1112 # device can still come up, it appears to be the old build and will
1113 # get set the OTA package again to retry.
1114 script.Print("Patching remaining system files...")
1115 for item in deferred_patch_list:
Michael Runge4038aa82013-12-13 18:06:28 -08001116 tf, sf, size, _ = item
1117 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
Nick Kralevich0eb17d92013-09-07 17:10:29 -07001118 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001119
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001120 if OPTIONS.two_step:
1121 script.AppendExtra("""
1122set_stage("%(bcb_dev)s", "");
1123endif;
1124endif;
1125""" % bcb_dev)
1126
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001127 script.AddToZip(target_zip, output_zip)
Doug Zongker2ea21062010-04-28 16:05:21 -07001128 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001129
1130
1131def main(argv):
1132
1133 def option_handler(o, a):
1134 if o in ("-b", "--board_config"):
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001135 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001136 elif o in ("-k", "--package_key"):
1137 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001138 elif o in ("-i", "--incremental_from"):
1139 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001140 elif o in ("-w", "--wipe_user_data"):
1141 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001142 elif o in ("-n", "--no_prereq"):
1143 OPTIONS.omit_prereq = True
Doug Zongker1c390a22009-05-14 19:06:36 -07001144 elif o in ("-e", "--extra_script"):
1145 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001146 elif o in ("-a", "--aslr_mode"):
1147 if a in ("on", "On", "true", "True", "yes", "Yes"):
1148 OPTIONS.aslr_mode = True
1149 else:
1150 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -07001151 elif o in ("--worker_threads"):
1152 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001153 elif o in ("-2", "--two_step"):
1154 OPTIONS.two_step = True
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001155 elif o in ("--no_signing"):
1156 OPTIONS.no_signing = True
Doug Zongkereef39442009-04-02 12:14:19 -07001157 else:
1158 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001159 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001160
1161 args = common.ParseOptions(argv, __doc__,
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001162 extra_opts="b:k:i:d:wne:a:2",
Doug Zongkereef39442009-04-02 12:14:19 -07001163 extra_long_opts=["board_config=",
1164 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001165 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001166 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001167 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001168 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001169 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001170 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001171 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001172 "no_signing",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001173 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001174 extra_option_handler=option_handler)
1175
1176 if len(args) != 2:
1177 common.Usage(__doc__)
1178 sys.exit(1)
1179
Doug Zongker1c390a22009-05-14 19:06:36 -07001180 if OPTIONS.extra_script is not None:
1181 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1182
Doug Zongkereef39442009-04-02 12:14:19 -07001183 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001184 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001185
Doug Zongkereef39442009-04-02 12:14:19 -07001186 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001187 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001188
1189 # If this image was originally labelled with SELinux contexts, make sure we
1190 # also apply the labels in our new image. During building, the "file_contexts"
1191 # is in the out/ directory tree, but for repacking from target-files.zip it's
1192 # in the root directory of the ramdisk.
1193 if "selinux_fc" in OPTIONS.info_dict:
1194 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1195 "file_contexts")
1196
Doug Zongker37974732010-09-16 17:44:38 -07001197 if OPTIONS.verbose:
1198 print "--- target info ---"
1199 common.DumpInfoDict(OPTIONS.info_dict)
1200
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001201 # If the caller explicitly specified the device-specific extensions
1202 # path via -s/--device_specific, use that. Otherwise, use
1203 # META/releasetools.py if it is present in the target target_files.
1204 # Otherwise, take the path of the file from 'tool_extensions' in the
1205 # info dict and look for that in the local filesystem, relative to
1206 # the current directory.
1207
Doug Zongker37974732010-09-16 17:44:38 -07001208 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001209 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1210 if os.path.exists(from_input):
1211 print "(using device-specific extensions from target_files)"
1212 OPTIONS.device_specific = from_input
1213 else:
1214 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1215
Doug Zongker37974732010-09-16 17:44:38 -07001216 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001217 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001218
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001219 if OPTIONS.no_signing:
1220 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1221 else:
1222 temp_zip_file = tempfile.NamedTemporaryFile()
1223 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1224 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001225
1226 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001227 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001228 if OPTIONS.package_key is None:
1229 OPTIONS.package_key = OPTIONS.info_dict.get(
1230 "default_system_dev_certificate",
1231 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001232 else:
1233 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001234 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001235 OPTIONS.target_info_dict = OPTIONS.info_dict
1236 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001237 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001238 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001239 "default_system_dev_certificate",
1240 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001241 if OPTIONS.verbose:
1242 print "--- source info ---"
1243 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001244 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001245
1246 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001247
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001248 if not OPTIONS.no_signing:
1249 SignOutput(temp_zip_file.name, args[1])
1250 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001251
1252 common.Cleanup()
1253
1254 print "done."
1255
1256
1257if __name__ == '__main__':
1258 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001259 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001260 main(sys.argv[1:])
1261 except common.ExternalError, e:
1262 print
1263 print " ERROR: %s" % (e,)
1264 print
1265 sys.exit(1)