blob: 6f344504b56b843d3af6b34131eec29bb494d7df [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")
382 if oem_props is None:
383 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")
465 oem_dict = None
466 if oem_props is not None:
467 if OPTIONS.oem_source is None:
468 raise common.ExternalError("OEM source required for this build")
469 script.Mount("/oem")
470 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
471
472 metadata = {"post-build": CalculateFingerprint(
473 oem_props, oem_dict, OPTIONS.info_dict),
474 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700475 OPTIONS.info_dict),
476 "post-timestamp": GetBuildProp("ro.build.date.utc",
477 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700478 }
479
Doug Zongker05d3dea2009-06-22 11:32:31 -0700480 device_specific = common.DeviceSpecificParams(
481 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700482 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700483 output_zip=output_zip,
484 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700485 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700486 metadata=metadata,
487 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700488
Doug Zongkerc9253822014-02-04 12:17:58 -0800489 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800490 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800491
Doug Zongker962069c2009-04-23 11:41:58 -0700492 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700493 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700494 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
495 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700496
Michael Runge6e836112014-04-15 17:40:21 -0700497 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700498 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800499
500 # Two-step package strategy (in chronological order, which is *not*
501 # the order in which the generated script has things):
502 #
503 # if stage is not "2/3" or "3/3":
504 # write recovery image to boot partition
505 # set stage to "2/3"
506 # reboot to boot partition and restart recovery
507 # else if stage is "2/3":
508 # write recovery image to recovery partition
509 # set stage to "3/3"
510 # reboot to recovery partition and restart recovery
511 # else:
512 # (stage must be "3/3")
513 # set stage to ""
514 # do normal full package installation:
515 # wipe and install system, boot image, etc.
516 # set up system to update recovery partition on first boot
517 # complete script normally (allow recovery to mark itself finished and reboot)
518
519 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
520 OPTIONS.input_tmp, "RECOVERY")
521 if OPTIONS.two_step:
522 if not OPTIONS.info_dict.get("multistage_support", None):
523 assert False, "two-step packages not supported by this build"
524 fs = OPTIONS.info_dict["fstab"]["/misc"]
525 assert fs.fs_type.upper() == "EMMC", \
526 "two-step packages only supported on devices with EMMC /misc partitions"
527 bcb_dev = {"bcb_dev": fs.device}
528 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
529 script.AppendExtra("""
530if get_stage("%(bcb_dev)s", "stage") == "2/3" then
531""" % bcb_dev)
532 script.WriteRawImage("/recovery", "recovery.img")
533 script.AppendExtra("""
534set_stage("%(bcb_dev)s", "3/3");
535reboot_now("%(bcb_dev)s", "recovery");
536else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
537""" % bcb_dev)
538
Doug Zongkere5ff5902012-01-17 10:55:37 -0800539 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700540
Doug Zongker01ce19c2014-02-04 13:48:15 -0800541 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700542
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700543 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800544 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700545 if HasVendorPartition(input_zip):
546 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700547
Kenny Rootf32dc712012-04-08 10:42:34 -0700548 if "selinux_fc" in OPTIONS.info_dict:
549 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500550
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700551 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700552 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800553 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700554 # Full OTA is done as an "incremental" against an empty source
555 # image. This has the effect of writing new data from the package
556 # to the entire partition, but lets us reuse the updater code that
557 # writes incrementals to do it.
558 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
559 system_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700560 system_diff = common.BlockDifference("system", system_tgt, src=None)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700561 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800562 else:
563 script.FormatPartition("/system")
564 script.Mount("/system")
565 if not has_recovery_patch:
566 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800567 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700568
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700569 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800570 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700571
Doug Zongker55d93282011-01-25 17:03:34 -0800572 boot_img = common.GetBootableImage("boot.img", "boot.img",
573 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800574
Doug Zongker91a99c22014-05-09 13:15:01 -0700575 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800576 def output_sink(fn, data):
577 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700578 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800579
580 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
581 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700582
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700583 system_items.GetMetadata(input_zip)
584 system_items.Get("system").SetPermissions(script)
585
586 if HasVendorPartition(input_zip):
587 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
588 script.ShowProgress(0.1, 0)
589
590 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700591 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
592 vendor_tgt.ResetFileMap()
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700593 vendor_diff = common.BlockDifference("vendor", vendor_tgt)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700594 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700595 else:
596 script.FormatPartition("/vendor")
597 script.Mount("/vendor")
598 script.UnpackPackageDir("vendor", "/vendor")
599
600 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
601 script.MakeSymlinks(symlinks)
602
603 vendor_items.GetMetadata(input_zip)
604 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700605
Doug Zongker37974732010-09-16 17:44:38 -0700606 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700607 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700608
Doug Zongker01ce19c2014-02-04 13:48:15 -0800609 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700610 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700611
Doug Zongker01ce19c2014-02-04 13:48:15 -0800612 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700613 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700614
Doug Zongker1c390a22009-05-14 19:06:36 -0700615 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700616 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700617
Doug Zongker14833602010-02-02 13:12:04 -0800618 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800619
Doug Zongker922206e2014-03-04 13:16:24 -0800620 if OPTIONS.wipe_user_data:
621 script.ShowProgress(0.1, 10)
622 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700623
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800624 if OPTIONS.two_step:
625 script.AppendExtra("""
626set_stage("%(bcb_dev)s", "");
627""" % bcb_dev)
628 script.AppendExtra("else\n")
629 script.WriteRawImage("/boot", "recovery.img")
630 script.AppendExtra("""
631set_stage("%(bcb_dev)s", "2/3");
632reboot_now("%(bcb_dev)s", "");
633endif;
634endif;
635""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800636 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700637 WriteMetadata(metadata, output_zip)
638
Doug Zongkerfc44a512014-08-26 13:10:25 -0700639
Stephen Smalley56882bf2012-02-09 13:36:21 -0500640def WritePolicyConfig(file_context, output_zip):
641 f = open(file_context, 'r');
642 basename = os.path.basename(file_context)
643 common.ZipWriteStr(output_zip, basename, f.read())
644
Doug Zongker2ea21062010-04-28 16:05:21 -0700645
646def WriteMetadata(metadata, output_zip):
647 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
648 "".join(["%s=%s\n" % kv
649 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700650
Doug Zongkerfc44a512014-08-26 13:10:25 -0700651
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700652def LoadPartitionFiles(z, partition):
653 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700654 ZipFile, and return a dict of {filename: File object}."""
655 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700656 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700657 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700658 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700659 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700660 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700661 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700662 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800663 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700664
665
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700666def GetBuildProp(prop, info_dict):
667 """Return the fingerprint of the build of a given target-files info_dict."""
668 try:
669 return info_dict.get("build.prop", {})[prop]
670 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700671 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700672
Doug Zongkerfc44a512014-08-26 13:10:25 -0700673
Michael Runge4038aa82013-12-13 18:06:28 -0800674def AddToKnownPaths(filename, known_paths):
675 if filename[-1] == "/":
676 return
677 dirs = filename.split("/")[:-1]
678 while len(dirs) > 0:
679 path = "/".join(dirs)
680 if path in known_paths:
681 break;
682 known_paths.add(path)
683 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700684
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700685
Geremy Condra36bd3652014-02-06 19:45:10 -0800686def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
687 source_version = OPTIONS.source_info_dict["recovery_api_version"]
688 target_version = OPTIONS.target_info_dict["recovery_api_version"]
689
690 if source_version == 0:
691 print ("WARNING: generating edify script for a source that "
692 "can't install it.")
693 script = edify_generator.EdifyGenerator(source_version,
694 OPTIONS.target_info_dict)
695
696 metadata = {"pre-device": GetBuildProp("ro.product.device",
697 OPTIONS.source_info_dict),
698 "post-timestamp": GetBuildProp("ro.build.date.utc",
699 OPTIONS.target_info_dict),
700 }
701
702 device_specific = common.DeviceSpecificParams(
703 source_zip=source_zip,
704 source_version=source_version,
705 target_zip=target_zip,
706 target_version=target_version,
707 output_zip=output_zip,
708 script=script,
709 metadata=metadata,
710 info_dict=OPTIONS.info_dict)
711
712 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
713 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
714 metadata["pre-build"] = source_fp
715 metadata["post-build"] = target_fp
716
717 source_boot = common.GetBootableImage(
718 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
719 OPTIONS.source_info_dict)
720 target_boot = common.GetBootableImage(
721 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
722 updating_boot = (not OPTIONS.two_step and
723 (source_boot.data != target_boot.data))
724
725 source_recovery = common.GetBootableImage(
726 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
727 OPTIONS.source_info_dict)
728 target_recovery = common.GetBootableImage(
729 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
730 updating_recovery = (source_recovery.data != target_recovery.data)
731
Doug Zongkerfc44a512014-08-26 13:10:25 -0700732 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
733 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700734 system_diff = common.BlockDifference("system", system_tgt, system_src,
735 check_first_block=True)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700736
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700737 if HasVendorPartition(target_zip):
738 if not HasVendorPartition(source_zip):
739 raise RuntimeError("can't generate incremental that adds /vendor")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700740 vendor_src = GetImage("vendor", OPTIONS.source_tmp, OPTIONS.source_info_dict)
741 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp, OPTIONS.target_info_dict)
Doug Zongkerb34fcce2014-09-11 09:34:56 -0700742 vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
743 check_first_block=True)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700744 else:
745 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800746
Michael Rungec6e3afd2014-05-05 11:55:47 -0700747 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
748 oem_dict = None
749 if oem_props is not None:
750 if OPTIONS.oem_source is None:
751 raise common.ExternalError("OEM source required for this build")
752 script.Mount("/oem")
753 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
754
755 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800756 device_specific.IncrementalOTA_Assertions()
757
758 # Two-step incremental package strategy (in chronological order,
759 # which is *not* the order in which the generated script has
760 # things):
761 #
762 # if stage is not "2/3" or "3/3":
763 # do verification on current system
764 # write recovery image to boot partition
765 # set stage to "2/3"
766 # reboot to boot partition and restart recovery
767 # else if stage is "2/3":
768 # write recovery image to recovery partition
769 # set stage to "3/3"
770 # reboot to recovery partition and restart recovery
771 # else:
772 # (stage must be "3/3")
773 # perform update:
774 # patch system files, etc.
775 # force full install of new boot image
776 # set up system to update recovery partition on first boot
777 # complete script normally (allow recovery to mark itself finished and reboot)
778
779 if OPTIONS.two_step:
780 if not OPTIONS.info_dict.get("multistage_support", None):
781 assert False, "two-step packages not supported by this build"
782 fs = OPTIONS.info_dict["fstab"]["/misc"]
783 assert fs.fs_type.upper() == "EMMC", \
784 "two-step packages only supported on devices with EMMC /misc partitions"
785 bcb_dev = {"bcb_dev": fs.device}
786 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
787 script.AppendExtra("""
788if get_stage("%(bcb_dev)s", "stage") == "2/3" then
789""" % bcb_dev)
790 script.AppendExtra("sleep(20);\n");
791 script.WriteRawImage("/recovery", "recovery.img")
792 script.AppendExtra("""
793set_stage("%(bcb_dev)s", "3/3");
794reboot_now("%(bcb_dev)s", "recovery");
795else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
796""" % bcb_dev)
797
798 script.Print("Verifying current system...")
799
800 device_specific.IncrementalOTA_VerifyBegin()
801
Michael Rungec6e3afd2014-05-05 11:55:47 -0700802 if oem_props is None:
803 script.AssertSomeFingerprint(source_fp, target_fp)
804 else:
805 script.AssertSomeThumbprint(
806 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
807 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800808
809 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700810 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800811 d = common.Difference(target_boot, source_boot)
812 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700813 if d is None:
814 include_full_boot = True
815 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
816 else:
817 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800818
Doug Zongkerf8340082014-08-05 10:39:37 -0700819 print "boot target: %d source: %d diff: %d" % (
820 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800821
Doug Zongkerf8340082014-08-05 10:39:37 -0700822 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800823
Doug Zongkerf8340082014-08-05 10:39:37 -0700824 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
825 (boot_type, boot_device,
826 source_boot.size, source_boot.sha1,
827 target_boot.size, target_boot.sha1))
Geremy Condra36bd3652014-02-06 19:45:10 -0800828
829 device_specific.IncrementalOTA_VerifyEnd()
830
831 if OPTIONS.two_step:
832 script.WriteRawImage("/boot", "recovery.img")
833 script.AppendExtra("""
834set_stage("%(bcb_dev)s", "2/3");
835reboot_now("%(bcb_dev)s", "");
836else
837""" % bcb_dev)
838
839 script.Comment("---- start making changes here ----")
840
841 device_specific.IncrementalOTA_InstallBegin()
842
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700843 system_diff.WriteScript(script, output_zip,
844 progress=0.8 if vendor_diff else 0.9)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700845 if vendor_diff:
Doug Zongkerab7ca1d2014-08-26 10:40:28 -0700846 vendor_diff.WriteScript(script, output_zip, progress=0.1)
Geremy Condra36bd3652014-02-06 19:45:10 -0800847
848 if OPTIONS.two_step:
849 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
850 script.WriteRawImage("/boot", "boot.img")
851 print "writing full boot image (forced by two-step mode)"
852
853 if not OPTIONS.two_step:
854 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700855 if include_full_boot:
856 print "boot image changed; including full."
857 script.Print("Installing boot image...")
858 script.WriteRawImage("/boot", "boot.img")
859 else:
860 # Produce the boot image by applying a patch to the current
861 # contents of the boot partition, and write it back to the
862 # partition.
863 print "boot image changed; including patch."
864 script.Print("Patching boot image...")
865 script.ShowProgress(0.1, 10)
866 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
867 % (boot_type, boot_device,
868 source_boot.size, source_boot.sha1,
869 target_boot.size, target_boot.sha1),
870 "-",
871 target_boot.size, target_boot.sha1,
872 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800873 else:
874 print "boot image unchanged; skipping."
875
876 # Do device-specific installation (eg, write radio image).
877 device_specific.IncrementalOTA_InstallEnd()
878
879 if OPTIONS.extra_script is not None:
880 script.AppendExtra(OPTIONS.extra_script)
881
Doug Zongker922206e2014-03-04 13:16:24 -0800882 if OPTIONS.wipe_user_data:
883 script.Print("Erasing user data...")
884 script.FormatPartition("/data")
885
Geremy Condra36bd3652014-02-06 19:45:10 -0800886 if OPTIONS.two_step:
887 script.AppendExtra("""
888set_stage("%(bcb_dev)s", "");
889endif;
890endif;
891""" % bcb_dev)
892
893 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800894 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800895 WriteMetadata(metadata, output_zip)
896
Doug Zongker32b527d2014-03-04 10:03:02 -0800897
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700898class FileDifference:
899 def __init__(self, partition, source_zip, target_zip, output_zip):
900 print "Loading target..."
901 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
902 print "Loading source..."
903 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
904
905 self.verbatim_targets = verbatim_targets = []
906 self.patch_list = patch_list = []
907 diffs = []
908 self.renames = renames = {}
909 known_paths = set()
910 largest_source_size = 0
911
912 matching_file_cache = {}
913 for fn, sf in source_data.items():
914 assert fn == sf.name
915 matching_file_cache["path:" + fn] = sf
916 if fn in target_data.keys():
917 AddToKnownPaths(fn, known_paths)
918 # Only allow eligibility for filename/sha matching
919 # if there isn't a perfect path match.
920 if target_data.get(sf.name) is None:
921 matching_file_cache["file:" + fn.split("/")[-1]] = sf
922 matching_file_cache["sha:" + sf.sha1] = sf
923
924 for fn in sorted(target_data.keys()):
925 tf = target_data[fn]
926 assert fn == tf.name
927 sf = ClosestFileMatch(tf, matching_file_cache, renames)
928 if sf is not None and sf.name != tf.name:
929 print "File has moved from " + sf.name + " to " + tf.name
930 renames[sf.name] = tf
931
932 if sf is None or fn in OPTIONS.require_verbatim:
933 # This file should be included verbatim
934 if fn in OPTIONS.prohibit_verbatim:
935 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
936 print "send", fn, "verbatim"
937 tf.AddToZip(output_zip)
938 verbatim_targets.append((fn, tf.size))
939 if fn in target_data.keys():
940 AddToKnownPaths(fn, known_paths)
941 elif tf.sha1 != sf.sha1:
942 # File is different; consider sending as a patch
943 diffs.append(common.Difference(tf, sf))
944 else:
945 # Target file data identical to source (may still be renamed)
946 pass
947
948 common.ComputeDifferences(diffs)
949
950 for diff in diffs:
951 tf, sf, d = diff.GetPatch()
952 path = "/".join(tf.name.split("/")[:-1])
953 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
954 path not in known_paths:
955 # patch is almost as big as the file; don't bother patching
956 # or a patch + rename cannot take place due to the target
957 # directory not existing
958 tf.AddToZip(output_zip)
959 verbatim_targets.append((tf.name, tf.size))
960 if sf.name in renames:
961 del renames[sf.name]
962 AddToKnownPaths(tf.name, known_paths)
963 else:
964 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
965 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
966 largest_source_size = max(largest_source_size, sf.size)
967
968 self.largest_source_size = largest_source_size
969
970 def EmitVerification(self, script):
971 so_far = 0
972 for tf, sf, size, patch_sha in self.patch_list:
973 if tf.name != sf.name:
974 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
975 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
976 so_far += sf.size
977 return so_far
978
979 def RemoveUnneededFiles(self, script, extras=()):
980 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
981 ["/"+i for i in sorted(self.source_data)
982 if i not in self.target_data and
983 i not in self.renames] +
984 list(extras))
985
986 def TotalPatchSize(self):
987 return sum(i[1].size for i in self.patch_list)
988
989 def EmitPatches(self, script, total_patch_size, so_far):
990 self.deferred_patch_list = deferred_patch_list = []
991 for item in self.patch_list:
992 tf, sf, size, _ = item
993 if tf.name == "system/build.prop":
994 deferred_patch_list.append(item)
995 continue
996 if (sf.name != tf.name):
997 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
998 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
999 so_far += tf.size
1000 script.SetProgress(so_far / total_patch_size)
1001 return so_far
1002
1003 def EmitDeferredPatches(self, script):
1004 for item in self.deferred_patch_list:
1005 tf, sf, size, _ = item
1006 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1007 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1008
1009 def EmitRenames(self, script):
1010 if len(self.renames) > 0:
1011 script.Print("Renaming files...")
1012 for src, tgt in self.renames.iteritems():
1013 print "Renaming " + src + " to " + tgt.name
1014 script.RenameFile(src, tgt.name)
1015
1016
1017
1018
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001019def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001020 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1021 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1022
Doug Zongker26e66192014-02-20 13:22:07 -08001023 if (OPTIONS.block_based and
1024 target_has_recovery_patch and
1025 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001026 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1027
Doug Zongker37974732010-09-16 17:44:38 -07001028 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1029 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001030
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001031 if source_version == 0:
1032 print ("WARNING: generating edify script for a source that "
1033 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001034 script = edify_generator.EdifyGenerator(source_version,
1035 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001036
Michael Runge6e836112014-04-15 17:40:21 -07001037 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1038 oem_dict = None
1039 if oem_props is not None:
1040 if OPTIONS.oem_source is None:
1041 raise common.ExternalError("OEM source required for this build")
1042 script.Mount("/oem")
1043 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1044
1045 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001046 OPTIONS.source_info_dict),
1047 "post-timestamp": GetBuildProp("ro.build.date.utc",
1048 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001049 }
1050
Doug Zongker05d3dea2009-06-22 11:32:31 -07001051 device_specific = common.DeviceSpecificParams(
1052 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001053 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001054 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001055 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001056 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001057 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001058 metadata=metadata,
1059 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001060
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001061 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge6e836112014-04-15 17:40:21 -07001062 script.Mount("/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001063 if HasVendorPartition(target_zip):
1064 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1065 script.Mount("/vendor")
1066 else:
1067 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001068
1069 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1070 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1071
1072 if oem_props is None:
1073 script.AssertSomeFingerprint(source_fp, target_fp)
1074 else:
1075 script.AssertSomeThumbprint(
1076 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1077 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1078
Doug Zongker2ea21062010-04-28 16:05:21 -07001079 metadata["pre-build"] = source_fp
1080 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001081
Doug Zongker55d93282011-01-25 17:03:34 -08001082 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001083 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1084 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001085 target_boot = common.GetBootableImage(
1086 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001087 updating_boot = (not OPTIONS.two_step and
1088 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001089
Doug Zongker55d93282011-01-25 17:03:34 -08001090 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001091 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1092 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001093 target_recovery = common.GetBootableImage(
1094 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001095 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001096
Doug Zongker881dd402009-09-20 14:03:55 -07001097 # Here's how we divide up the progress bar:
1098 # 0.1 for verifying the start state (PatchCheck calls)
1099 # 0.8 for applying patches (ApplyPatch calls)
1100 # 0.1 for unpacking verbatim files, symlinking, and doing the
1101 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001102
Michael Runge6e836112014-04-15 17:40:21 -07001103 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001104 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001105
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001106 # Two-step incremental package strategy (in chronological order,
1107 # which is *not* the order in which the generated script has
1108 # things):
1109 #
1110 # if stage is not "2/3" or "3/3":
1111 # do verification on current system
1112 # write recovery image to boot partition
1113 # set stage to "2/3"
1114 # reboot to boot partition and restart recovery
1115 # else if stage is "2/3":
1116 # write recovery image to recovery partition
1117 # set stage to "3/3"
1118 # reboot to recovery partition and restart recovery
1119 # else:
1120 # (stage must be "3/3")
1121 # perform update:
1122 # patch system files, etc.
1123 # force full install of new boot image
1124 # set up system to update recovery partition on first boot
1125 # complete script normally (allow recovery to mark itself finished and reboot)
1126
1127 if OPTIONS.two_step:
1128 if not OPTIONS.info_dict.get("multistage_support", None):
1129 assert False, "two-step packages not supported by this build"
1130 fs = OPTIONS.info_dict["fstab"]["/misc"]
1131 assert fs.fs_type.upper() == "EMMC", \
1132 "two-step packages only supported on devices with EMMC /misc partitions"
1133 bcb_dev = {"bcb_dev": fs.device}
1134 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1135 script.AppendExtra("""
1136if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1137""" % bcb_dev)
1138 script.AppendExtra("sleep(20);\n");
1139 script.WriteRawImage("/recovery", "recovery.img")
1140 script.AppendExtra("""
1141set_stage("%(bcb_dev)s", "3/3");
1142reboot_now("%(bcb_dev)s", "recovery");
1143else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1144""" % bcb_dev)
1145
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001146 script.Print("Verifying current system...")
1147
Doug Zongkere5ff5902012-01-17 10:55:37 -08001148 device_specific.IncrementalOTA_VerifyBegin()
1149
Doug Zongker881dd402009-09-20 14:03:55 -07001150 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001151 so_far = system_diff.EmitVerification(script)
1152 if vendor_diff:
1153 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001154
Doug Zongker5da317e2009-06-02 13:38:17 -07001155 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001156 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001157 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001158 print "boot target: %d source: %d diff: %d" % (
1159 target_boot.size, source_boot.size, len(d))
1160
Doug Zongker048e7ca2009-06-15 14:31:53 -07001161 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001162
Doug Zongker96a57e72010-09-26 14:57:41 -07001163 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001164
1165 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1166 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001167 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001168 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001169 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001170
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001171 size = []
1172 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1173 if vendor_diff:
1174 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1175 if size or updating_recovery or updating_boot:
1176 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001177
Doug Zongker05d3dea2009-06-22 11:32:31 -07001178 device_specific.IncrementalOTA_VerifyEnd()
1179
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001180 if OPTIONS.two_step:
1181 script.WriteRawImage("/boot", "recovery.img")
1182 script.AppendExtra("""
1183set_stage("%(bcb_dev)s", "2/3");
1184reboot_now("%(bcb_dev)s", "");
1185else
1186""" % bcb_dev)
1187
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001188 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001189
Doug Zongkere5ff5902012-01-17 10:55:37 -08001190 device_specific.IncrementalOTA_InstallBegin()
1191
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001192 if OPTIONS.two_step:
1193 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1194 script.WriteRawImage("/boot", "boot.img")
1195 print "writing full boot image (forced by two-step mode)"
1196
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001197 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001198 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1199 if vendor_diff:
1200 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001201
Doug Zongker881dd402009-09-20 14:03:55 -07001202 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001203 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1204 if vendor_diff:
1205 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001206 if updating_boot:
1207 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001208
1209 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001210 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1211 if vendor_diff:
1212 script.Print("Patching vendor files...")
1213 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001214
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001215 if not OPTIONS.two_step:
1216 if updating_boot:
1217 # Produce the boot image by applying a patch to the current
1218 # contents of the boot partition, and write it back to the
1219 # partition.
1220 script.Print("Patching boot image...")
1221 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1222 % (boot_type, boot_device,
1223 source_boot.size, source_boot.sha1,
1224 target_boot.size, target_boot.sha1),
1225 "-",
1226 target_boot.size, target_boot.sha1,
1227 source_boot.sha1, "patch/boot.img.p")
1228 so_far += target_boot.size
1229 script.SetProgress(so_far / total_patch_size)
1230 print "boot image changed; including."
1231 else:
1232 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001233
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001234 system_items = ItemSet("system", "META/filesystem_config.txt")
1235 if vendor_diff:
1236 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1237
Doug Zongkereef39442009-04-02 12:14:19 -07001238 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001239 # Recovery is generated as a patch using both the boot image
1240 # (which contains the same linux kernel as recovery) and the file
1241 # /system/etc/recovery-resource.dat (which contains all the images
1242 # used in the recovery UI) as sources. This lets us minimize the
1243 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001244 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001245 # For older builds where recovery-resource.dat is not present, we
1246 # use only the boot image as the source.
1247
Doug Zongkerc9253822014-02-04 12:17:58 -08001248 if not target_has_recovery_patch:
1249 def output_sink(fn, data):
1250 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001251 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001252
1253 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1254 target_recovery, target_boot)
1255 script.DeleteFiles(["/system/recovery-from-boot.p",
1256 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001257 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001258 else:
1259 print "recovery image unchanged; skipping."
1260
Doug Zongker881dd402009-09-20 14:03:55 -07001261 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001262
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001263 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1264 if vendor_diff:
1265 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1266
1267 temp_script = script.MakeTemporary()
1268 system_items.GetMetadata(target_zip)
1269 system_items.Get("system").SetPermissions(temp_script)
1270 if vendor_diff:
1271 vendor_items.GetMetadata(target_zip)
1272 vendor_items.Get("vendor").SetPermissions(temp_script)
1273
1274 # Note that this call will mess up the trees of Items, so make sure
1275 # we're done with them.
1276 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1277 if vendor_diff:
1278 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001279
1280 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001281 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1282
1283 # Delete all the symlinks in source that aren't in target. This
1284 # needs to happen before verbatim files are unpacked, in case a
1285 # symlink in the source is replaced by a real file in the target.
1286 to_delete = []
1287 for dest, link in source_symlinks:
1288 if link not in target_symlinks_d:
1289 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001290 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001291
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001292 if system_diff.verbatim_targets:
1293 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001294 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001295 if vendor_diff and vendor_diff.verbatim_targets:
1296 script.Print("Unpacking new vendor files...")
1297 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001298
Doug Zongkerc9253822014-02-04 12:17:58 -08001299 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001300 script.Print("Unpacking new recovery...")
1301 script.UnpackPackageDir("recovery", "/system")
1302
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001303 system_diff.EmitRenames(script)
1304 if vendor_diff:
1305 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001306
Doug Zongker05d3dea2009-06-22 11:32:31 -07001307 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001308
1309 # Create all the symlinks that don't already exist, or point to
1310 # somewhere different than what we want. Delete each symlink before
1311 # creating it, since the 'symlink' command won't overwrite.
1312 to_create = []
1313 for dest, link in target_symlinks:
1314 if link in source_symlinks_d:
1315 if dest != source_symlinks_d[link]:
1316 to_create.append((dest, link))
1317 else:
1318 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001319 script.DeleteFiles([i[1] for i in to_create])
1320 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001321
1322 # Now that the symlinks are created, we can set all the
1323 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001324 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001325
Doug Zongker881dd402009-09-20 14:03:55 -07001326 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001327 device_specific.IncrementalOTA_InstallEnd()
1328
Doug Zongker1c390a22009-05-14 19:06:36 -07001329 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001330 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001331
Doug Zongkere92f15a2011-08-26 13:46:40 -07001332 # Patch the build.prop file last, so if something fails but the
1333 # device can still come up, it appears to be the old build and will
1334 # get set the OTA package again to retry.
1335 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001336 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001337
Doug Zongker922206e2014-03-04 13:16:24 -08001338 if OPTIONS.wipe_user_data:
1339 script.Print("Erasing user data...")
1340 script.FormatPartition("/data")
1341
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001342 if OPTIONS.two_step:
1343 script.AppendExtra("""
1344set_stage("%(bcb_dev)s", "");
1345endif;
1346endif;
1347""" % bcb_dev)
1348
Doug Zongker25568482014-03-03 10:21:27 -08001349 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001350 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001351
1352
1353def main(argv):
1354
1355 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001356 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001357 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001358 elif o in ("-k", "--package_key"):
1359 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001360 elif o in ("-i", "--incremental_from"):
1361 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001362 elif o in ("-w", "--wipe_user_data"):
1363 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001364 elif o in ("-n", "--no_prereq"):
1365 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001366 elif o in ("-o", "--oem_settings"):
1367 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001368 elif o in ("-e", "--extra_script"):
1369 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001370 elif o in ("-a", "--aslr_mode"):
1371 if a in ("on", "On", "true", "True", "yes", "Yes"):
1372 OPTIONS.aslr_mode = True
1373 else:
1374 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001375 elif o in ("-t", "--worker_threads"):
1376 if a.isdigit():
1377 OPTIONS.worker_threads = int(a)
1378 else:
1379 raise ValueError("Cannot parse value %r for option %r - only "
1380 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001381 elif o in ("-2", "--two_step"):
1382 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001383 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001384 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001385 elif o == "--block":
1386 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001387 elif o in ("-b", "--binary"):
1388 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001389 elif o in ("--no_fallback_to_full",):
1390 OPTIONS.fallback_to_full = False
Doug Zongkereef39442009-04-02 12:14:19 -07001391 else:
1392 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001393 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001394
1395 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001396 extra_opts="b:k:i:d:wne:t:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001397 extra_long_opts=["board_config=",
1398 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001399 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001400 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001401 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001402 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001403 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001404 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001405 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001406 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001407 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001408 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001409 "oem_settings=",
Doug Zongker62d4f182014-08-04 16:06:43 -07001410 "no_fallback_to_full",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001411 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001412 extra_option_handler=option_handler)
1413
1414 if len(args) != 2:
1415 common.Usage(__doc__)
1416 sys.exit(1)
1417
Doug Zongker1c390a22009-05-14 19:06:36 -07001418 if OPTIONS.extra_script is not None:
1419 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1420
Doug Zongkereef39442009-04-02 12:14:19 -07001421 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001422 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001423
Doug Zongkereef39442009-04-02 12:14:19 -07001424 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001425 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001426
1427 # If this image was originally labelled with SELinux contexts, make sure we
1428 # also apply the labels in our new image. During building, the "file_contexts"
1429 # is in the out/ directory tree, but for repacking from target-files.zip it's
1430 # in the root directory of the ramdisk.
1431 if "selinux_fc" in OPTIONS.info_dict:
1432 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1433 "file_contexts")
1434
Doug Zongker37974732010-09-16 17:44:38 -07001435 if OPTIONS.verbose:
1436 print "--- target info ---"
1437 common.DumpInfoDict(OPTIONS.info_dict)
1438
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001439 # If the caller explicitly specified the device-specific extensions
1440 # path via -s/--device_specific, use that. Otherwise, use
1441 # META/releasetools.py if it is present in the target target_files.
1442 # Otherwise, take the path of the file from 'tool_extensions' in the
1443 # info dict and look for that in the local filesystem, relative to
1444 # the current directory.
1445
Doug Zongker37974732010-09-16 17:44:38 -07001446 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001447 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1448 if os.path.exists(from_input):
1449 print "(using device-specific extensions from target_files)"
1450 OPTIONS.device_specific = from_input
1451 else:
1452 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1453
Doug Zongker37974732010-09-16 17:44:38 -07001454 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001455 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001456
Doug Zongker62d4f182014-08-04 16:06:43 -07001457 while True:
Doug Zongkereef39442009-04-02 12:14:19 -07001458
Doug Zongker62d4f182014-08-04 16:06:43 -07001459 if OPTIONS.no_signing:
1460 if os.path.exists(args[1]): os.unlink(args[1])
1461 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1462 else:
1463 temp_zip_file = tempfile.NamedTemporaryFile()
1464 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1465 compression=zipfile.ZIP_DEFLATED)
1466
1467 if OPTIONS.incremental_source is None:
1468 WriteFullOTAPackage(input_zip, output_zip)
1469 if OPTIONS.package_key is None:
1470 OPTIONS.package_key = OPTIONS.info_dict.get(
1471 "default_system_dev_certificate",
1472 "build/target/product/security/testkey")
1473 break
1474
1475 else:
1476 print "unzipping source target-files..."
1477 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
1478 OPTIONS.target_info_dict = OPTIONS.info_dict
1479 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1480 if "selinux_fc" in OPTIONS.source_info_dict:
1481 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1482 "file_contexts")
1483 if OPTIONS.package_key is None:
1484 OPTIONS.package_key = OPTIONS.source_info_dict.get(
1485 "default_system_dev_certificate",
1486 "build/target/product/security/testkey")
1487 if OPTIONS.verbose:
1488 print "--- source info ---"
1489 common.DumpInfoDict(OPTIONS.source_info_dict)
1490 try:
1491 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
1492 break
1493 except ValueError:
1494 if not OPTIONS.fallback_to_full: raise
1495 print "--- failed to build incremental; falling back to full ---"
1496 OPTIONS.incremental_source = None
1497 output_zip.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001498
1499 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001500
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001501 if not OPTIONS.no_signing:
1502 SignOutput(temp_zip_file.name, args[1])
1503 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001504
Doug Zongkereef39442009-04-02 12:14:19 -07001505 print "done."
1506
1507
1508if __name__ == '__main__':
1509 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001510 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001511 main(sys.argv[1:])
1512 except common.ExternalError, e:
1513 print
1514 print " ERROR: %s" % (e,)
1515 print
1516 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001517 finally:
1518 common.Cleanup()