blob: 2a3ab89fd24a94698b141ab7aeac81fc3c150001 [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
Doug Zongker25568482014-03-03 10:21:27 -080024 --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
Michael Runge6e836112014-04-15 17:40:21 -070040 -o (--oem_settings) <file>
41 Use the file to specify the expected OEM-specific properties
42 on the OEM partition of the intended device.
43
Doug Zongkerdbfaae52009-04-21 17:12:54 -070044 -w (--wipe_user_data)
45 Generate an OTA package that will wipe the user data partition
46 when installed.
47
Doug Zongker962069c2009-04-23 11:41:58 -070048 -n (--no_prereq)
49 Omit the timestamp prereq check normally included at the top of
50 the build scripts (used for developer OTA packages which
51 legitimately need to go back and forth).
52
Doug Zongker1c390a22009-05-14 19:06:36 -070053 -e (--extra_script) <file>
54 Insert the contents of file at the end of the update script.
55
Hristo Bojinovdafb0422010-08-26 14:35:16 -070056 -a (--aslr_mode) <on|off>
57 Specify whether to turn on ASLR for the package (on by default).
Stephen Smalley56882bf2012-02-09 13:36:21 -050058
Doug Zongker9b23f2c2013-11-25 14:44:12 -080059 -2 (--two_step)
60 Generate a 'two-step' OTA package, where recovery is updated
61 first, so that any changes made to the system partition are done
62 using the new recovery (new kernel, etc.).
63
Doug Zongker26e66192014-02-20 13:22:07 -080064 --block
65 Generate a block-based OTA if possible. Will fall back to a
66 file-based OTA if the target_files is older and doesn't support
67 block-based OTAs.
68
Doug Zongker25568482014-03-03 10:21:27 -080069 -b (--binary) <file>
70 Use the given binary as the update-binary in the output package,
71 instead of the binary in the build's target_files. Use for
72 development only.
73
Martin Blumenstingl374e1142014-05-31 20:42:55 +020074 -t (--worker_threads) <int>
75 Specifies the number of worker-threads that will be used when
76 generating patches for incremental updates (defaults to 3).
77
Doug Zongkereef39442009-04-02 12:14:19 -070078"""
79
80import sys
81
Doug Zongkercf6d5a92014-02-18 10:57:07 -080082if sys.hexversion < 0x02070000:
83 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -070084 sys.exit(1)
85
86import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070087import errno
Doug Zongkerfc44a512014-08-26 13:10:25 -070088import multiprocessing
Doug Zongkereef39442009-04-02 12:14:19 -070089import os
90import re
Doug Zongkereef39442009-04-02 12:14:19 -070091import subprocess
92import tempfile
93import time
94import zipfile
95
Doug Zongkerfc44a512014-08-26 13:10:25 -070096from hashlib import sha1 as sha1
davidcad0bb92011-03-15 14:21:38 +000097
Doug Zongkereef39442009-04-02 12:14:19 -070098import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -070099import edify_generator
Geremy Condra36bd3652014-02-06 19:45:10 -0800100import build_image
Doug Zongkerfc44a512014-08-26 13:10:25 -0700101import blockimgdiff
102import sparse_img
Doug Zongkereef39442009-04-02 12:14:19 -0700103
104OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700105OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700106OPTIONS.incremental_source = None
107OPTIONS.require_verbatim = set()
108OPTIONS.prohibit_verbatim = set(("system/build.prop",))
109OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700110OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700111OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700112OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700113OPTIONS.aslr_mode = True
Doug Zongkerfc44a512014-08-26 13:10:25 -0700114OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
115if OPTIONS.worker_threads == 0:
116 OPTIONS.worker_threads = 1
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800117OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900118OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800119OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800120OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700121OPTIONS.oem_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -0700122OPTIONS.fallback_to_full = True
Doug Zongkereef39442009-04-02 12:14:19 -0700123
124def MostPopularKey(d, default):
125 """Given a dict, return the key corresponding to the largest
126 value. Returns 'default' if the dict is empty."""
127 x = [(v, k) for (k, v) in d.iteritems()]
128 if not x: return default
129 x.sort()
130 return x[-1][1]
131
132
133def IsSymlink(info):
134 """Return true if the zipfile.ZipInfo object passed in represents a
135 symlink."""
136 return (info.external_attr >> 16) == 0120777
137
Hristo Bojinov96be7202010-08-02 10:26:17 -0700138def IsRegular(info):
139 """Return true if the zipfile.ZipInfo object passed in represents a
140 symlink."""
141 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700142
Michael Runge4038aa82013-12-13 18:06:28 -0800143def ClosestFileMatch(src, tgtfiles, existing):
144 """Returns the closest file match between a source file and list
145 of potential matches. The exact filename match is preferred,
146 then the sha1 is searched for, and finally a file with the same
147 basename is evaluated. Rename support in the updater-binary is
148 required for the latter checks to be used."""
149
150 result = tgtfiles.get("path:" + src.name)
151 if result is not None:
152 return result
153
154 if not OPTIONS.target_info_dict.get("update_rename_support", False):
155 return None
156
157 if src.size < 1000:
158 return None
159
160 result = tgtfiles.get("sha1:" + src.sha1)
161 if result is not None and existing.get(result.name) is None:
162 return result
163 result = tgtfiles.get("file:" + src.name.split("/")[-1])
164 if result is not None and existing.get(result.name) is None:
165 return result
166 return None
167
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700168class ItemSet:
169 def __init__(self, partition, fs_config):
170 self.partition = partition
171 self.fs_config = fs_config
172 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700173
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700174 def Get(self, name, dir=False):
175 if name not in self.ITEMS:
176 self.ITEMS[name] = Item(self, name, dir=dir)
177 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700178
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700179 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700180 # The target_files contains a record of what the uid,
181 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700182 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700183
184 for line in output.split("\n"):
185 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700186 columns = line.split()
187 name, uid, gid, mode = columns[:4]
188 selabel = None
189 capabilities = None
190
191 # After the first 4 columns, there are a series of key=value
192 # pairs. Extract out the fields we care about.
193 for element in columns[4:]:
194 key, value = element.split("=")
195 if key == "selabel":
196 selabel = value
197 if key == "capabilities":
198 capabilities = value
199
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700200 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700201 if i is not None:
202 i.uid = int(uid)
203 i.gid = int(gid)
204 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700205 i.selabel = selabel
206 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700207 if i.dir:
208 i.children.sort(key=lambda i: i.name)
209
210 # set metadata for the files generated by this script.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700211 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700212 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700213 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700214 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700215
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700216
217class Item:
218 """Items represent the metadata (user, group, mode) of files and
219 directories in the system image."""
220 def __init__(self, itemset, name, dir=False):
221 self.itemset = itemset
222 self.name = name
223 self.uid = None
224 self.gid = None
225 self.mode = None
226 self.selabel = None
227 self.capabilities = None
228 self.dir = dir
229
230 if name:
231 self.parent = itemset.Get(os.path.dirname(name), dir=True)
232 self.parent.children.append(self)
233 else:
234 self.parent = None
235 if dir:
236 self.children = []
237
238 def Dump(self, indent=0):
239 if self.uid is not None:
240 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
241 else:
242 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
243 if self.dir:
244 print "%s%s" % (" "*indent, self.descendants)
245 print "%s%s" % (" "*indent, self.best_subtree)
246 for i in self.children:
247 i.Dump(indent=indent+1)
248
Doug Zongkereef39442009-04-02 12:14:19 -0700249 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700250 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
251 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700252 set_perm to correctly chown/chmod all the files to their desired
253 values. Recursively calls itself for all descendants.
254
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700255 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700256 all descendants of this node. (dmode or fmode may be None.) Also
257 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700258 dmode, fmode, selabel, capabilities) tuple that will match the most
259 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700260 """
261
262 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700263 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700264 for i in self.children:
265 if i.dir:
266 for k, v in i.CountChildMetadata().iteritems():
267 d[k] = d.get(k, 0) + v
268 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700269 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700270 d[k] = d.get(k, 0) + 1
271
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700272 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
273 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700274
275 # First, find the (uid, gid) pair that matches the most
276 # descendants.
277 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700278 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700279 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
280 ug = MostPopularKey(ug, (0, 0))
281
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700282 # Now find the dmode, fmode, selabel, and capabilities that match
283 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700284 best_dmode = (0, 0755)
285 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700286 best_selabel = (0, None)
287 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700288 for k, count in d.iteritems():
289 if k[:2] != ug: continue
290 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
291 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700292 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
293 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
294 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700295
296 return d
297
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700298 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700299 """Append set_perm/set_perm_recursive commands to 'script' to
300 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700301 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700302
303 self.CountChildMetadata()
304
305 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700306 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700307 # item (and all its children) have already been set to. We only
308 # need to issue set_perm/set_perm_recursive commands if we're
309 # supposed to be something different.
310 if item.dir:
311 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700312 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700313 current = item.best_subtree
314
315 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700316 item.mode != current[2] or item.selabel != current[4] or \
317 item.capabilities != current[5]:
318 script.SetPermissions("/"+item.name, item.uid, item.gid,
319 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700320
321 for i in item.children:
322 recurse(i, current)
323 else:
324 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700325 item.mode != current[3] or item.selabel != current[4] or \
326 item.capabilities != current[5]:
327 script.SetPermissions("/"+item.name, item.uid, item.gid,
328 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700329
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700330 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700331
332
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700333def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
334 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700335 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800336 list of symlinks. output_zip may be None, in which case the copy is
337 skipped (but the other side effects still happen). substitute is an
338 optional dict of {output filename: contents} to be output instead of
339 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700340 """
341
342 symlinks = []
343
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700344 partition = itemset.partition
345
Doug Zongkereef39442009-04-02 12:14:19 -0700346 for info in input_zip.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700347 if info.filename.startswith(partition.upper() + "/"):
Doug Zongkereef39442009-04-02 12:14:19 -0700348 basefilename = info.filename[7:]
349 if IsSymlink(info):
350 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700351 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700352 else:
353 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700354 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700355 if substitute and fn in substitute and substitute[fn] is None:
356 continue
357 if output_zip is not None:
358 if substitute and fn in substitute:
359 data = substitute[fn]
360 else:
361 data = input_zip.read(info.filename)
362 output_zip.writestr(info2, data)
363 if fn.endswith("/"):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700364 itemset.Get(fn[:-1], dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700365 else:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700366 itemset.Get(fn, dir=False)
Doug Zongkereef39442009-04-02 12:14:19 -0700367
368 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800369 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700370
371
Doug Zongkereef39442009-04-02 12:14:19 -0700372def SignOutput(temp_zip_name, output_zip_name):
373 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
374 pw = key_passwords[OPTIONS.package_key]
375
Doug Zongker951495f2009-08-14 12:44:19 -0700376 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
377 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700378
379
Michael Rungec6e3afd2014-05-05 11:55:47 -0700380def AppendAssertions(script, info_dict, oem_dict = None):
Michael Runge6e836112014-04-15 17:40:21 -0700381 oem_props = info_dict.get("oem_fingerprint_properties")
Michael Runge560569a2014-09-18 15:12:45 -0700382 if oem_props is None or len(oem_props) == 0:
Michael Runge6e836112014-04-15 17:40:21 -0700383 device = GetBuildProp("ro.product.device", info_dict)
384 script.AssertDevice(device)
385 else:
386 if oem_dict is None:
387 raise common.ExternalError("No OEM file provided to answer expected assertions")
388 for prop in oem_props.split():
389 if oem_dict.get(prop) is None:
390 raise common.ExternalError("The OEM file is missing the property %s" % prop)
391 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700392
Doug Zongkereef39442009-04-02 12:14:19 -0700393
Doug Zongkerc9253822014-02-04 12:17:58 -0800394def HasRecoveryPatch(target_files_zip):
395 try:
396 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
397 return True
398 except KeyError:
399 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700400
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700401def HasVendorPartition(target_files_zip):
402 try:
403 target_files_zip.getinfo("VENDOR/")
404 return True
405 except KeyError:
406 return False
407
Michael Runge6e836112014-04-15 17:40:21 -0700408def GetOemProperty(name, oem_props, oem_dict, info_dict):
409 if oem_props is not None and name in oem_props:
410 return oem_dict[name]
411 return GetBuildProp(name, info_dict)
412
413
414def CalculateFingerprint(oem_props, oem_dict, info_dict):
415 if oem_props is None:
416 return GetBuildProp("ro.build.fingerprint", info_dict)
417 return "%s/%s/%s:%s" % (
418 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
419 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
420 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
421 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700422
Doug Zongkerfc44a512014-08-26 13:10:25 -0700423
Doug Zongker3c84f562014-07-31 11:06:30 -0700424def GetImage(which, tmpdir, info_dict):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700425 # Return an image object (suitable for passing to BlockImageDiff)
426 # for the 'which' partition (most be "system" or "vendor"). If a
427 # prebuilt image and file map are found in tmpdir they are used,
428 # otherwise they are reconstructed from the individual files.
Doug Zongker3c84f562014-07-31 11:06:30 -0700429
430 assert which in ("system", "vendor")
431
432 path = os.path.join(tmpdir, "IMAGES", which + ".img")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700433 mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
434 if os.path.exists(path) and os.path.exists(mappath):
Doug Zongker3c84f562014-07-31 11:06:30 -0700435 print "using %s.img from target-files" % (which,)
Doug Zongker3c84f562014-07-31 11:06:30 -0700436 # This is a 'new' target-files, which already has the image in it.
Doug Zongker3c84f562014-07-31 11:06:30 -0700437
438 else:
439 print "building %s.img from target-files" % (which,)
440
441 # This is an 'old' target-files, which does not contain images
442 # already built. Build them.
443
Doug Zongkerfc44a512014-08-26 13:10:25 -0700444 mappath = tempfile.mkstemp()[1]
445 OPTIONS.tempfiles.append(mappath)
446
Doug Zongker3c84f562014-07-31 11:06:30 -0700447 import add_img_to_target_files
448 if which == "system":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700449 path = add_img_to_target_files.BuildSystem(
450 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700451 elif which == "vendor":
Doug Zongkerfc44a512014-08-26 13:10:25 -0700452 path = add_img_to_target_files.BuildVendor(
453 tmpdir, info_dict, block_list=mappath)
Doug Zongker3c84f562014-07-31 11:06:30 -0700454
Doug Zongkerfc44a512014-08-26 13:10:25 -0700455 return sparse_img.SparseImage(path, mappath)
456
457
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700458def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700459 # TODO: how to determine this? We don't know what version it will
460 # be installed on top of. For now, we expect the API just won't
461 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700462 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700463
Michael Runge6e836112014-04-15 17:40:21 -0700464 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700465 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -0700466 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700467 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -0700468 if OPTIONS.oem_source is None:
469 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700470 script.Mount("/oem", recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -0700471 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
472
473 metadata = {"post-build": CalculateFingerprint(
474 oem_props, oem_dict, OPTIONS.info_dict),
475 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700476 OPTIONS.info_dict),
477 "post-timestamp": GetBuildProp("ro.build.date.utc",
478 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700479 }
480
Doug Zongker05d3dea2009-06-22 11:32:31 -0700481 device_specific = common.DeviceSpecificParams(
482 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700483 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700484 output_zip=output_zip,
485 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700486 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700487 metadata=metadata,
488 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700489
Doug Zongkerc9253822014-02-04 12:17:58 -0800490 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800491 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800492
Doug Zongker962069c2009-04-23 11:41:58 -0700493 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700494 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700495 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
496 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700497
Michael Runge6e836112014-04-15 17:40:21 -0700498 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700499 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800500
501 # Two-step package strategy (in chronological order, which is *not*
502 # the order in which the generated script has things):
503 #
504 # if stage is not "2/3" or "3/3":
505 # write recovery image to boot partition
506 # set stage to "2/3"
507 # reboot to boot partition and restart recovery
508 # else if stage is "2/3":
509 # write recovery image to recovery partition
510 # set stage to "3/3"
511 # reboot to recovery partition and restart recovery
512 # else:
513 # (stage must be "3/3")
514 # set stage to ""
515 # do normal full package installation:
516 # wipe and install system, boot image, etc.
517 # set up system to update recovery partition on first boot
518 # complete script normally (allow recovery to mark itself finished and reboot)
519
520 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
521 OPTIONS.input_tmp, "RECOVERY")
522 if OPTIONS.two_step:
523 if not OPTIONS.info_dict.get("multistage_support", None):
524 assert False, "two-step packages not supported by this build"
525 fs = OPTIONS.info_dict["fstab"]["/misc"]
526 assert fs.fs_type.upper() == "EMMC", \
527 "two-step packages only supported on devices with EMMC /misc partitions"
528 bcb_dev = {"bcb_dev": fs.device}
529 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
530 script.AppendExtra("""
531if get_stage("%(bcb_dev)s", "stage") == "2/3" then
532""" % bcb_dev)
533 script.WriteRawImage("/recovery", "recovery.img")
534 script.AppendExtra("""
535set_stage("%(bcb_dev)s", "3/3");
536reboot_now("%(bcb_dev)s", "recovery");
537else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
538""" % bcb_dev)
539
Doug Zongkere5ff5902012-01-17 10:55:37 -0800540 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700541
Doug Zongker01ce19c2014-02-04 13:48:15 -0800542 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700543
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700544 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800545 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700546 if HasVendorPartition(input_zip):
547 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700548
Kenny Rootf32dc712012-04-08 10:42:34 -0700549 if "selinux_fc" in OPTIONS.info_dict:
550 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500551
Michael Runge7cd99ba2014-10-22 17:21:48 -0700552 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
553
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700554 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700555 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800556 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700557 # Full OTA is done as an "incremental" against an empty source
558 # image. This has the effect of writing new data from the package
559 # to the entire partition, but lets us reuse the updater code that
560 # writes incrementals to do it.
561 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
562 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700563 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700564 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800565 else:
566 script.FormatPartition("/system")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700567 script.Mount("/system", recovery_mount_options)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800568 if not has_recovery_patch:
569 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800570 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700571
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700572 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800573 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700574
Doug Zongker55d93282011-01-25 17:03:34 -0800575 boot_img = common.GetBootableImage("boot.img", "boot.img",
576 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800577
Doug Zongker91a99c22014-05-09 13:15:01 -0700578 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800579 def output_sink(fn, data):
580 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700581 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800582
583 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
584 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700585
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700586 system_items.GetMetadata(input_zip)
587 system_items.Get("system").SetPermissions(script)
588
589 if HasVendorPartition(input_zip):
590 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
591 script.ShowProgress(0.1, 0)
592
593 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700594 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
595 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700596 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700597 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700598 else:
599 script.FormatPartition("/vendor")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700600 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700601 script.UnpackPackageDir("vendor", "/vendor")
602
603 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
604 script.MakeSymlinks(symlinks)
605
606 vendor_items.GetMetadata(input_zip)
607 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700608
Doug Zongker37974732010-09-16 17:44:38 -0700609 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700610 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700611
Doug Zongker01ce19c2014-02-04 13:48:15 -0800612 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700613 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700614
Doug Zongker01ce19c2014-02-04 13:48:15 -0800615 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700616 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700617
Doug Zongker1c390a22009-05-14 19:06:36 -0700618 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700619 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700620
Doug Zongker14833602010-02-02 13:12:04 -0800621 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800622
Doug Zongker922206e2014-03-04 13:16:24 -0800623 if OPTIONS.wipe_user_data:
624 script.ShowProgress(0.1, 10)
625 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700626
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800627 if OPTIONS.two_step:
628 script.AppendExtra("""
629set_stage("%(bcb_dev)s", "");
630""" % bcb_dev)
631 script.AppendExtra("else\n")
632 script.WriteRawImage("/boot", "recovery.img")
633 script.AppendExtra("""
634set_stage("%(bcb_dev)s", "2/3");
635reboot_now("%(bcb_dev)s", "");
636endif;
637endif;
638""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800639 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700640 WriteMetadata(metadata, output_zip)
641
Doug Zongkerfc44a512014-08-26 13:10:25 -0700642
Stephen Smalley56882bf2012-02-09 13:36:21 -0500643def WritePolicyConfig(file_context, output_zip):
644 f = open(file_context, 'r');
645 basename = os.path.basename(file_context)
646 common.ZipWriteStr(output_zip, basename, f.read())
647
Doug Zongker2ea21062010-04-28 16:05:21 -0700648
649def WriteMetadata(metadata, output_zip):
650 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
651 "".join(["%s=%s\n" % kv
652 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700653
Doug Zongkerfc44a512014-08-26 13:10:25 -0700654
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700655def LoadPartitionFiles(z, partition):
656 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700657 ZipFile, and return a dict of {filename: File object}."""
658 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700659 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700660 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700661 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700662 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700663 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700664 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700665 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800666 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700667
668
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700669def GetBuildProp(prop, info_dict):
670 """Return the fingerprint of the build of a given target-files info_dict."""
671 try:
672 return info_dict.get("build.prop", {})[prop]
673 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700674 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700675
Doug Zongkerfc44a512014-08-26 13:10:25 -0700676
Michael Runge4038aa82013-12-13 18:06:28 -0800677def AddToKnownPaths(filename, known_paths):
678 if filename[-1] == "/":
679 return
680 dirs = filename.split("/")[:-1]
681 while len(dirs) > 0:
682 path = "/".join(dirs)
683 if path in known_paths:
684 break;
685 known_paths.add(path)
686 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700687
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700688
Geremy Condra36bd3652014-02-06 19:45:10 -0800689def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
690 source_version = OPTIONS.source_info_dict["recovery_api_version"]
691 target_version = OPTIONS.target_info_dict["recovery_api_version"]
692
693 if source_version == 0:
694 print ("WARNING: generating edify script for a source that "
695 "can't install it.")
696 script = edify_generator.EdifyGenerator(source_version,
697 OPTIONS.target_info_dict)
698
699 metadata = {"pre-device": GetBuildProp("ro.product.device",
700 OPTIONS.source_info_dict),
701 "post-timestamp": GetBuildProp("ro.build.date.utc",
702 OPTIONS.target_info_dict),
703 }
704
705 device_specific = common.DeviceSpecificParams(
706 source_zip=source_zip,
707 source_version=source_version,
708 target_zip=target_zip,
709 target_version=target_version,
710 output_zip=output_zip,
711 script=script,
712 metadata=metadata,
713 info_dict=OPTIONS.info_dict)
714
715 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
716 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
717 metadata["pre-build"] = source_fp
718 metadata["post-build"] = target_fp
719
720 source_boot = common.GetBootableImage(
721 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
722 OPTIONS.source_info_dict)
723 target_boot = common.GetBootableImage(
724 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
725 updating_boot = (not OPTIONS.two_step and
726 (source_boot.data != target_boot.data))
727
728 source_recovery = common.GetBootableImage(
729 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
730 OPTIONS.source_info_dict)
731 target_recovery = common.GetBootableImage(
732 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
733 updating_recovery = (source_recovery.data != target_recovery.data)
734
Doug Zongkerfc44a512014-08-26 13:10:25 -0700735 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
736 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700737 system_diff = common.BlockDifference("system", system_tgt, system_src,
738 check_first_block=True)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700739
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700740 if HasVendorPartition(target_zip):
741 if not HasVendorPartition(source_zip):
742 raise RuntimeError("can't generate incremental that adds /vendor")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700743 vendor_src = GetImage("vendor", OPTIONS.source_tmp, OPTIONS.source_info_dict)
744 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700745 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
746 check_first_block=True)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700747 else:
748 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800749
Michael Rungec6e3afd2014-05-05 11:55:47 -0700750 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700751 recovery_mount_options = OPTIONS.target_info_dict.get("recovery_mount_options")
Michael Rungec6e3afd2014-05-05 11:55:47 -0700752 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -0700753 if oem_props is not None and len(oem_props) > 0:
Michael Rungec6e3afd2014-05-05 11:55:47 -0700754 if OPTIONS.oem_source is None:
755 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -0700756 script.Mount("/oem", recovery_mount_options)
Michael Rungec6e3afd2014-05-05 11:55:47 -0700757 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
758
759 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800760 device_specific.IncrementalOTA_Assertions()
761
762 # Two-step incremental package strategy (in chronological order,
763 # which is *not* the order in which the generated script has
764 # things):
765 #
766 # if stage is not "2/3" or "3/3":
767 # do verification on current system
768 # write recovery image to boot partition
769 # set stage to "2/3"
770 # reboot to boot partition and restart recovery
771 # else if stage is "2/3":
772 # write recovery image to recovery partition
773 # set stage to "3/3"
774 # reboot to recovery partition and restart recovery
775 # else:
776 # (stage must be "3/3")
777 # perform update:
778 # patch system files, etc.
779 # force full install of new boot image
780 # set up system to update recovery partition on first boot
781 # complete script normally (allow recovery to mark itself finished and reboot)
782
783 if OPTIONS.two_step:
784 if not OPTIONS.info_dict.get("multistage_support", None):
785 assert False, "two-step packages not supported by this build"
786 fs = OPTIONS.info_dict["fstab"]["/misc"]
787 assert fs.fs_type.upper() == "EMMC", \
788 "two-step packages only supported on devices with EMMC /misc partitions"
789 bcb_dev = {"bcb_dev": fs.device}
790 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
791 script.AppendExtra("""
792if get_stage("%(bcb_dev)s", "stage") == "2/3" then
793""" % bcb_dev)
794 script.AppendExtra("sleep(20);\n");
795 script.WriteRawImage("/recovery", "recovery.img")
796 script.AppendExtra("""
797set_stage("%(bcb_dev)s", "3/3");
798reboot_now("%(bcb_dev)s", "recovery");
799else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
800""" % bcb_dev)
801
802 script.Print("Verifying current system...")
803
804 device_specific.IncrementalOTA_VerifyBegin()
805
Michael Rungec6e3afd2014-05-05 11:55:47 -0700806 if oem_props is None:
807 script.AssertSomeFingerprint(source_fp, target_fp)
808 else:
809 script.AssertSomeThumbprint(
810 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
811 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800812
813 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700814 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800815 d = common.Difference(target_boot, source_boot)
816 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700817 if d is None:
818 include_full_boot = True
819 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
820 else:
821 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800822
Doug Zongkerf8340082014-08-05 10:39:37 -0700823 print "boot target: %d source: %d diff: %d" % (
824 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800825
Doug Zongkerf8340082014-08-05 10:39:37 -0700826 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800827
Doug Zongkerf8340082014-08-05 10:39:37 -0700828 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
829 (boot_type, boot_device,
830 source_boot.size, source_boot.sha1,
831 target_boot.size, target_boot.sha1))
Geremy Condra36bd3652014-02-06 19:45:10 -0800832
833 device_specific.IncrementalOTA_VerifyEnd()
834
835 if OPTIONS.two_step:
836 script.WriteRawImage("/boot", "recovery.img")
837 script.AppendExtra("""
838set_stage("%(bcb_dev)s", "2/3");
839reboot_now("%(bcb_dev)s", "");
840else
841""" % bcb_dev)
842
843 script.Comment("---- start making changes here ----")
844
845 device_specific.IncrementalOTA_InstallBegin()
846
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700847 system_diff.WriteScript(script, output_zip,
848 progress=0.8 if vendor_diff else 0.9)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700849 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700850 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800851
852 if OPTIONS.two_step:
853 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
854 script.WriteRawImage("/boot", "boot.img")
855 print "writing full boot image (forced by two-step mode)"
856
857 if not OPTIONS.two_step:
858 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700859 if include_full_boot:
860 print "boot image changed; including full."
861 script.Print("Installing boot image...")
862 script.WriteRawImage("/boot", "boot.img")
863 else:
864 # Produce the boot image by applying a patch to the current
865 # contents of the boot partition, and write it back to the
866 # partition.
867 print "boot image changed; including patch."
868 script.Print("Patching boot image...")
869 script.ShowProgress(0.1, 10)
870 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
871 % (boot_type, boot_device,
872 source_boot.size, source_boot.sha1,
873 target_boot.size, target_boot.sha1),
874 "-",
875 target_boot.size, target_boot.sha1,
876 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800877 else:
878 print "boot image unchanged; skipping."
879
880 # Do device-specific installation (eg, write radio image).
881 device_specific.IncrementalOTA_InstallEnd()
882
883 if OPTIONS.extra_script is not None:
884 script.AppendExtra(OPTIONS.extra_script)
885
Doug Zongker922206e2014-03-04 13:16:24 -0800886 if OPTIONS.wipe_user_data:
887 script.Print("Erasing user data...")
888 script.FormatPartition("/data")
889
Geremy Condra36bd3652014-02-06 19:45:10 -0800890 if OPTIONS.two_step:
891 script.AppendExtra("""
892set_stage("%(bcb_dev)s", "");
893endif;
894endif;
895""" % bcb_dev)
896
897 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800898 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800899 WriteMetadata(metadata, output_zip)
900
Doug Zongker32b527d2014-03-04 10:03:02 -0800901
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700902class FileDifference:
903 def __init__(self, partition, source_zip, target_zip, output_zip):
904 print "Loading target..."
905 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
906 print "Loading source..."
907 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
908
909 self.verbatim_targets = verbatim_targets = []
910 self.patch_list = patch_list = []
911 diffs = []
912 self.renames = renames = {}
913 known_paths = set()
914 largest_source_size = 0
915
916 matching_file_cache = {}
917 for fn, sf in source_data.items():
918 assert fn == sf.name
919 matching_file_cache["path:" + fn] = sf
920 if fn in target_data.keys():
921 AddToKnownPaths(fn, known_paths)
922 # Only allow eligibility for filename/sha matching
923 # if there isn't a perfect path match.
924 if target_data.get(sf.name) is None:
925 matching_file_cache["file:" + fn.split("/")[-1]] = sf
926 matching_file_cache["sha:" + sf.sha1] = sf
927
928 for fn in sorted(target_data.keys()):
929 tf = target_data[fn]
930 assert fn == tf.name
931 sf = ClosestFileMatch(tf, matching_file_cache, renames)
932 if sf is not None and sf.name != tf.name:
933 print "File has moved from " + sf.name + " to " + tf.name
934 renames[sf.name] = tf
935
936 if sf is None or fn in OPTIONS.require_verbatim:
937 # This file should be included verbatim
938 if fn in OPTIONS.prohibit_verbatim:
939 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
940 print "send", fn, "verbatim"
941 tf.AddToZip(output_zip)
942 verbatim_targets.append((fn, tf.size))
943 if fn in target_data.keys():
944 AddToKnownPaths(fn, known_paths)
945 elif tf.sha1 != sf.sha1:
946 # File is different; consider sending as a patch
947 diffs.append(common.Difference(tf, sf))
948 else:
949 # Target file data identical to source (may still be renamed)
950 pass
951
952 common.ComputeDifferences(diffs)
953
954 for diff in diffs:
955 tf, sf, d = diff.GetPatch()
956 path = "/".join(tf.name.split("/")[:-1])
957 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
958 path not in known_paths:
959 # patch is almost as big as the file; don't bother patching
960 # or a patch + rename cannot take place due to the target
961 # directory not existing
962 tf.AddToZip(output_zip)
963 verbatim_targets.append((tf.name, tf.size))
964 if sf.name in renames:
965 del renames[sf.name]
966 AddToKnownPaths(tf.name, known_paths)
967 else:
968 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
969 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
970 largest_source_size = max(largest_source_size, sf.size)
971
972 self.largest_source_size = largest_source_size
973
974 def EmitVerification(self, script):
975 so_far = 0
976 for tf, sf, size, patch_sha in self.patch_list:
977 if tf.name != sf.name:
978 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
979 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
980 so_far += sf.size
981 return so_far
982
983 def RemoveUnneededFiles(self, script, extras=()):
984 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
985 ["/"+i for i in sorted(self.source_data)
986 if i not in self.target_data and
987 i not in self.renames] +
988 list(extras))
989
990 def TotalPatchSize(self):
991 return sum(i[1].size for i in self.patch_list)
992
993 def EmitPatches(self, script, total_patch_size, so_far):
994 self.deferred_patch_list = deferred_patch_list = []
995 for item in self.patch_list:
996 tf, sf, size, _ = item
997 if tf.name == "system/build.prop":
998 deferred_patch_list.append(item)
999 continue
1000 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")
1003 so_far += tf.size
1004 script.SetProgress(so_far / total_patch_size)
1005 return so_far
1006
1007 def EmitDeferredPatches(self, script):
1008 for item in self.deferred_patch_list:
1009 tf, sf, size, _ = item
1010 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1011 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1012
1013 def EmitRenames(self, script):
1014 if len(self.renames) > 0:
1015 script.Print("Renaming files...")
1016 for src, tgt in self.renames.iteritems():
1017 print "Renaming " + src + " to " + tgt.name
1018 script.RenameFile(src, tgt.name)
1019
1020
1021
1022
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001023def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001024 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1025 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1026
Doug Zongker26e66192014-02-20 13:22:07 -08001027 if (OPTIONS.block_based and
1028 target_has_recovery_patch and
1029 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001030 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1031
Doug Zongker37974732010-09-16 17:44:38 -07001032 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1033 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001034
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001035 if source_version == 0:
1036 print ("WARNING: generating edify script for a source that "
1037 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001038 script = edify_generator.EdifyGenerator(source_version,
1039 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001040
Michael Runge6e836112014-04-15 17:40:21 -07001041 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
Michael Runge7cd99ba2014-10-22 17:21:48 -07001042 recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
Michael Runge6e836112014-04-15 17:40:21 -07001043 oem_dict = None
Michael Runge560569a2014-09-18 15:12:45 -07001044 if oem_props is not None and len(oem_props) > 0:
Michael Runge6e836112014-04-15 17:40:21 -07001045 if OPTIONS.oem_source is None:
1046 raise common.ExternalError("OEM source required for this build")
Michael Runge7cd99ba2014-10-22 17:21:48 -07001047 script.Mount("/oem", recovery_mount_options)
Michael Runge6e836112014-04-15 17:40:21 -07001048 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1049
1050 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001051 OPTIONS.source_info_dict),
1052 "post-timestamp": GetBuildProp("ro.build.date.utc",
1053 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001054 }
1055
Doug Zongker05d3dea2009-06-22 11:32:31 -07001056 device_specific = common.DeviceSpecificParams(
1057 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001058 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001059 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001060 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001061 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001062 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001063 metadata=metadata,
1064 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001065
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001066 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001067 script.Mount("/system", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001068 if HasVendorPartition(target_zip):
1069 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
Michael Runge7cd99ba2014-10-22 17:21:48 -07001070 script.Mount("/vendor", recovery_mount_options)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001071 else:
1072 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001073
1074 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1075 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1076
1077 if oem_props is None:
1078 script.AssertSomeFingerprint(source_fp, target_fp)
1079 else:
1080 script.AssertSomeThumbprint(
1081 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1082 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1083
Doug Zongker2ea21062010-04-28 16:05:21 -07001084 metadata["pre-build"] = source_fp
1085 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001086
Doug Zongker55d93282011-01-25 17:03:34 -08001087 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001088 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1089 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001090 target_boot = common.GetBootableImage(
1091 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001092 updating_boot = (not OPTIONS.two_step and
1093 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001094
Doug Zongker55d93282011-01-25 17:03:34 -08001095 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001096 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1097 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001098 target_recovery = common.GetBootableImage(
1099 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001100 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001101
Doug Zongker881dd402009-09-20 14:03:55 -07001102 # Here's how we divide up the progress bar:
1103 # 0.1 for verifying the start state (PatchCheck calls)
1104 # 0.8 for applying patches (ApplyPatch calls)
1105 # 0.1 for unpacking verbatim files, symlinking, and doing the
1106 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001107
Michael Runge6e836112014-04-15 17:40:21 -07001108 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001109 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001110
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001111 # Two-step incremental package strategy (in chronological order,
1112 # which is *not* the order in which the generated script has
1113 # things):
1114 #
1115 # if stage is not "2/3" or "3/3":
1116 # do verification on current system
1117 # write recovery image to boot partition
1118 # set stage to "2/3"
1119 # reboot to boot partition and restart recovery
1120 # else if stage is "2/3":
1121 # write recovery image to recovery partition
1122 # set stage to "3/3"
1123 # reboot to recovery partition and restart recovery
1124 # else:
1125 # (stage must be "3/3")
1126 # perform update:
1127 # patch system files, etc.
1128 # force full install of new boot image
1129 # set up system to update recovery partition on first boot
1130 # complete script normally (allow recovery to mark itself finished and reboot)
1131
1132 if OPTIONS.two_step:
1133 if not OPTIONS.info_dict.get("multistage_support", None):
1134 assert False, "two-step packages not supported by this build"
1135 fs = OPTIONS.info_dict["fstab"]["/misc"]
1136 assert fs.fs_type.upper() == "EMMC", \
1137 "two-step packages only supported on devices with EMMC /misc partitions"
1138 bcb_dev = {"bcb_dev": fs.device}
1139 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1140 script.AppendExtra("""
1141if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1142""" % bcb_dev)
1143 script.AppendExtra("sleep(20);\n");
1144 script.WriteRawImage("/recovery", "recovery.img")
1145 script.AppendExtra("""
1146set_stage("%(bcb_dev)s", "3/3");
1147reboot_now("%(bcb_dev)s", "recovery");
1148else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1149""" % bcb_dev)
1150
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001151 script.Print("Verifying current system...")
1152
Doug Zongkere5ff5902012-01-17 10:55:37 -08001153 device_specific.IncrementalOTA_VerifyBegin()
1154
Doug Zongker881dd402009-09-20 14:03:55 -07001155 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001156 so_far = system_diff.EmitVerification(script)
1157 if vendor_diff:
1158 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001159
Doug Zongker5da317e2009-06-02 13:38:17 -07001160 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001161 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001162 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001163 print "boot target: %d source: %d diff: %d" % (
1164 target_boot.size, source_boot.size, len(d))
1165
Doug Zongker048e7ca2009-06-15 14:31:53 -07001166 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001167
Doug Zongker96a57e72010-09-26 14:57:41 -07001168 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001169
1170 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1171 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001172 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001173 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001174 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001175
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001176 size = []
1177 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1178 if vendor_diff:
1179 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1180 if size or updating_recovery or updating_boot:
1181 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001182
Doug Zongker05d3dea2009-06-22 11:32:31 -07001183 device_specific.IncrementalOTA_VerifyEnd()
1184
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001185 if OPTIONS.two_step:
1186 script.WriteRawImage("/boot", "recovery.img")
1187 script.AppendExtra("""
1188set_stage("%(bcb_dev)s", "2/3");
1189reboot_now("%(bcb_dev)s", "");
1190else
1191""" % bcb_dev)
1192
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001193 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001194
Doug Zongkere5ff5902012-01-17 10:55:37 -08001195 device_specific.IncrementalOTA_InstallBegin()
1196
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001197 if OPTIONS.two_step:
1198 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1199 script.WriteRawImage("/boot", "boot.img")
1200 print "writing full boot image (forced by two-step mode)"
1201
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001202 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001203 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1204 if vendor_diff:
1205 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001206
Doug Zongker881dd402009-09-20 14:03:55 -07001207 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001208 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1209 if vendor_diff:
1210 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001211 if updating_boot:
1212 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001213
1214 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001215 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1216 if vendor_diff:
1217 script.Print("Patching vendor files...")
1218 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001219
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001220 if not OPTIONS.two_step:
1221 if updating_boot:
1222 # Produce the boot image by applying a patch to the current
1223 # contents of the boot partition, and write it back to the
1224 # partition.
1225 script.Print("Patching boot image...")
1226 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1227 % (boot_type, boot_device,
1228 source_boot.size, source_boot.sha1,
1229 target_boot.size, target_boot.sha1),
1230 "-",
1231 target_boot.size, target_boot.sha1,
1232 source_boot.sha1, "patch/boot.img.p")
1233 so_far += target_boot.size
1234 script.SetProgress(so_far / total_patch_size)
1235 print "boot image changed; including."
1236 else:
1237 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001238
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001239 system_items = ItemSet("system", "META/filesystem_config.txt")
1240 if vendor_diff:
1241 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1242
Doug Zongkereef39442009-04-02 12:14:19 -07001243 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001244 # Recovery is generated as a patch using both the boot image
1245 # (which contains the same linux kernel as recovery) and the file
1246 # /system/etc/recovery-resource.dat (which contains all the images
1247 # used in the recovery UI) as sources. This lets us minimize the
1248 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001249 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001250 # For older builds where recovery-resource.dat is not present, we
1251 # use only the boot image as the source.
1252
Doug Zongkerc9253822014-02-04 12:17:58 -08001253 if not target_has_recovery_patch:
1254 def output_sink(fn, data):
1255 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001256 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001257
1258 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1259 target_recovery, target_boot)
1260 script.DeleteFiles(["/system/recovery-from-boot.p",
1261 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001262 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001263 else:
1264 print "recovery image unchanged; skipping."
1265
Doug Zongker881dd402009-09-20 14:03:55 -07001266 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001267
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001268 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1269 if vendor_diff:
1270 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1271
1272 temp_script = script.MakeTemporary()
1273 system_items.GetMetadata(target_zip)
1274 system_items.Get("system").SetPermissions(temp_script)
1275 if vendor_diff:
1276 vendor_items.GetMetadata(target_zip)
1277 vendor_items.Get("vendor").SetPermissions(temp_script)
1278
1279 # Note that this call will mess up the trees of Items, so make sure
1280 # we're done with them.
1281 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1282 if vendor_diff:
1283 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001284
1285 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001286 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1287
1288 # Delete all the symlinks in source that aren't in target. This
1289 # needs to happen before verbatim files are unpacked, in case a
1290 # symlink in the source is replaced by a real file in the target.
1291 to_delete = []
1292 for dest, link in source_symlinks:
1293 if link not in target_symlinks_d:
1294 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001295 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001296
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001297 if system_diff.verbatim_targets:
1298 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001299 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001300 if vendor_diff and vendor_diff.verbatim_targets:
1301 script.Print("Unpacking new vendor files...")
1302 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001303
Doug Zongkerc9253822014-02-04 12:17:58 -08001304 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001305 script.Print("Unpacking new recovery...")
1306 script.UnpackPackageDir("recovery", "/system")
1307
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001308 system_diff.EmitRenames(script)
1309 if vendor_diff:
1310 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001311
Doug Zongker05d3dea2009-06-22 11:32:31 -07001312 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001313
1314 # Create all the symlinks that don't already exist, or point to
1315 # somewhere different than what we want. Delete each symlink before
1316 # creating it, since the 'symlink' command won't overwrite.
1317 to_create = []
1318 for dest, link in target_symlinks:
1319 if link in source_symlinks_d:
1320 if dest != source_symlinks_d[link]:
1321 to_create.append((dest, link))
1322 else:
1323 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001324 script.DeleteFiles([i[1] for i in to_create])
1325 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001326
1327 # Now that the symlinks are created, we can set all the
1328 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001329 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001330
Doug Zongker881dd402009-09-20 14:03:55 -07001331 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001332 device_specific.IncrementalOTA_InstallEnd()
1333
Doug Zongker1c390a22009-05-14 19:06:36 -07001334 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001335 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001336
Doug Zongkere92f15a2011-08-26 13:46:40 -07001337 # Patch the build.prop file last, so if something fails but the
1338 # device can still come up, it appears to be the old build and will
1339 # get set the OTA package again to retry.
1340 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001341 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001342
Doug Zongker922206e2014-03-04 13:16:24 -08001343 if OPTIONS.wipe_user_data:
1344 script.Print("Erasing user data...")
1345 script.FormatPartition("/data")
1346
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001347 if OPTIONS.two_step:
1348 script.AppendExtra("""
1349set_stage("%(bcb_dev)s", "");
1350endif;
1351endif;
1352""" % bcb_dev)
1353
Doug Zongker25568482014-03-03 10:21:27 -08001354 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001355 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001356
1357
1358def main(argv):
1359
1360 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001361 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001362 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001363 elif o in ("-k", "--package_key"):
1364 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001365 elif o in ("-i", "--incremental_from"):
1366 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001367 elif o in ("-w", "--wipe_user_data"):
1368 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001369 elif o in ("-n", "--no_prereq"):
1370 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001371 elif o in ("-o", "--oem_settings"):
1372 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001373 elif o in ("-e", "--extra_script"):
1374 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001375 elif o in ("-a", "--aslr_mode"):
1376 if a in ("on", "On", "true", "True", "yes", "Yes"):
1377 OPTIONS.aslr_mode = True
1378 else:
1379 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001380 elif o in ("-t", "--worker_threads"):
1381 if a.isdigit():
1382 OPTIONS.worker_threads = int(a)
1383 else:
1384 raise ValueError("Cannot parse value %r for option %r - only "
1385 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001386 elif o in ("-2", "--two_step"):
1387 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001388 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001389 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001390 elif o == "--block":
1391 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001392 elif o in ("-b", "--binary"):
1393 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001394 elif o in ("--no_fallback_to_full",):
1395 OPTIONS.fallback_to_full = False
Doug Zongkereef39442009-04-02 12:14:19 -07001396 else:
1397 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001398 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001399
1400 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001401 extra_opts="b:k:i:d:wne:t:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001402 extra_long_opts=["board_config=",
1403 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001404 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001405 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001406 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001407 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001408 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001409 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001410 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001411 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001412 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001413 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001414 "oem_settings=",
Doug Zongker62d4f182014-08-04 16:06:43 -07001415 "no_fallback_to_full",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001416 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001417 extra_option_handler=option_handler)
1418
1419 if len(args) != 2:
1420 common.Usage(__doc__)
1421 sys.exit(1)
1422
Doug Zongker1c390a22009-05-14 19:06:36 -07001423 if OPTIONS.extra_script is not None:
1424 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1425
Doug Zongkereef39442009-04-02 12:14:19 -07001426 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001427 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001428
Doug Zongkereef39442009-04-02 12:14:19 -07001429 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001430 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001431
1432 # If this image was originally labelled with SELinux contexts, make sure we
1433 # also apply the labels in our new image. During building, the "file_contexts"
1434 # is in the out/ directory tree, but for repacking from target-files.zip it's
1435 # in the root directory of the ramdisk.
1436 if "selinux_fc" in OPTIONS.info_dict:
1437 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1438 "file_contexts")
1439
Doug Zongker37974732010-09-16 17:44:38 -07001440 if OPTIONS.verbose:
1441 print "--- target info ---"
1442 common.DumpInfoDict(OPTIONS.info_dict)
1443
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001444 # If the caller explicitly specified the device-specific extensions
1445 # path via -s/--device_specific, use that. Otherwise, use
1446 # META/releasetools.py if it is present in the target target_files.
1447 # Otherwise, take the path of the file from 'tool_extensions' in the
1448 # info dict and look for that in the local filesystem, relative to
1449 # the current directory.
1450
Doug Zongker37974732010-09-16 17:44:38 -07001451 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001452 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1453 if os.path.exists(from_input):
1454 print "(using device-specific extensions from target_files)"
1455 OPTIONS.device_specific = from_input
1456 else:
1457 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1458
Doug Zongker37974732010-09-16 17:44:38 -07001459 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001460 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001461
Doug Zongker62d4f182014-08-04 16:06:43 -07001462 while True:
Doug Zongkereef39442009-04-02 12:14:19 -07001463
Doug Zongker62d4f182014-08-04 16:06:43 -07001464 if OPTIONS.no_signing:
1465 if os.path.exists(args[1]): os.unlink(args[1])
1466 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1467 else:
1468 temp_zip_file = tempfile.NamedTemporaryFile()
1469 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1470 compression=zipfile.ZIP_DEFLATED)
1471
1472 if OPTIONS.incremental_source is None:
1473 WriteFullOTAPackage(input_zip, output_zip)
1474 if OPTIONS.package_key is None:
1475 OPTIONS.package_key = OPTIONS.info_dict.get(
1476 "default_system_dev_certificate",
1477 "build/target/product/security/testkey")
1478 break
1479
1480 else:
1481 print "unzipping source target-files..."
1482 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
1483 OPTIONS.target_info_dict = OPTIONS.info_dict
1484 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1485 if "selinux_fc" in OPTIONS.source_info_dict:
1486 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1487 "file_contexts")
1488 if OPTIONS.package_key is None:
1489 OPTIONS.package_key = OPTIONS.source_info_dict.get(
1490 "default_system_dev_certificate",
1491 "build/target/product/security/testkey")
1492 if OPTIONS.verbose:
1493 print "--- source info ---"
1494 common.DumpInfoDict(OPTIONS.source_info_dict)
1495 try:
1496 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
1497 break
1498 except ValueError:
1499 if not OPTIONS.fallback_to_full: raise
1500 print "--- failed to build incremental; falling back to full ---"
1501 OPTIONS.incremental_source = None
1502 output_zip.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001503
1504 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001505
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001506 if not OPTIONS.no_signing:
1507 SignOutput(temp_zip_file.name, args[1])
1508 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001509
Doug Zongkereef39442009-04-02 12:14:19 -07001510 print "done."
1511
1512
1513if __name__ == '__main__':
1514 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001515 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001516 main(sys.argv[1:])
1517 except common.ExternalError, e:
1518 print
1519 print " ERROR: %s" % (e,)
1520 print
1521 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001522 finally:
1523 common.Cleanup()