blob: bcc3210f36ffa5daeb0d62b6d73a622d0ccf3aee [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
458class BlockDifference:
459 def __init__(self, partition, tgt, src=None):
460 self.partition = partition
461
462 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads)
463 tmpdir = tempfile.mkdtemp()
464 OPTIONS.tempfiles.append(tmpdir)
465 self.path = os.path.join(tmpdir, partition)
466 b.Compute(self.path)
467
468 _, self.device = common.GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
469
470 def WriteScript(self, script, output_zip):
471 partition = self.partition
472 with open(self.path + ".transfer.list", "rb") as f:
473 common.ZipWriteStr(output_zip, partition + ".transfer.list", f.read())
474 with open(self.path + ".new.dat", "rb") as f:
475 common.ZipWriteStr(output_zip, partition + ".new.dat", f.read())
476 with open(self.path + ".patch.dat", "rb") as f:
477 common.ZipWriteStr(output_zip, partition + ".patch.dat", f.read(),
478 compression=zipfile.ZIP_STORED)
479
480 call = (('block_image_update("%s", '
481 'package_extract_file("%s.transfer.list"), '
482 '"%s.new.dat", "%s.patch.dat");\n') %
483 (self.device, partition, partition, partition))
484 script.AppendExtra(script._WordWrap(call))
Doug Zongker3c84f562014-07-31 11:06:30 -0700485
486
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700487def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700488 # TODO: how to determine this? We don't know what version it will
489 # be installed on top of. For now, we expect the API just won't
490 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700491 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700492
Michael Runge6e836112014-04-15 17:40:21 -0700493 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
494 oem_dict = None
495 if oem_props is not None:
496 if OPTIONS.oem_source is None:
497 raise common.ExternalError("OEM source required for this build")
498 script.Mount("/oem")
499 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
500
501 metadata = {"post-build": CalculateFingerprint(
502 oem_props, oem_dict, OPTIONS.info_dict),
503 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700504 OPTIONS.info_dict),
505 "post-timestamp": GetBuildProp("ro.build.date.utc",
506 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700507 }
508
Doug Zongker05d3dea2009-06-22 11:32:31 -0700509 device_specific = common.DeviceSpecificParams(
510 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700511 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700512 output_zip=output_zip,
513 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700514 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700515 metadata=metadata,
516 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700517
Doug Zongkerc9253822014-02-04 12:17:58 -0800518 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800519 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800520
Doug Zongker962069c2009-04-23 11:41:58 -0700521 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700522 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700523 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
524 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700525
Michael Runge6e836112014-04-15 17:40:21 -0700526 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700527 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800528
529 # Two-step package strategy (in chronological order, which is *not*
530 # the order in which the generated script has things):
531 #
532 # if stage is not "2/3" or "3/3":
533 # write recovery image to boot partition
534 # set stage to "2/3"
535 # reboot to boot partition and restart recovery
536 # else if stage is "2/3":
537 # write recovery image to recovery partition
538 # set stage to "3/3"
539 # reboot to recovery partition and restart recovery
540 # else:
541 # (stage must be "3/3")
542 # set stage to ""
543 # do normal full package installation:
544 # wipe and install system, boot image, etc.
545 # set up system to update recovery partition on first boot
546 # complete script normally (allow recovery to mark itself finished and reboot)
547
548 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
549 OPTIONS.input_tmp, "RECOVERY")
550 if OPTIONS.two_step:
551 if not OPTIONS.info_dict.get("multistage_support", None):
552 assert False, "two-step packages not supported by this build"
553 fs = OPTIONS.info_dict["fstab"]["/misc"]
554 assert fs.fs_type.upper() == "EMMC", \
555 "two-step packages only supported on devices with EMMC /misc partitions"
556 bcb_dev = {"bcb_dev": fs.device}
557 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
558 script.AppendExtra("""
559if get_stage("%(bcb_dev)s", "stage") == "2/3" then
560""" % bcb_dev)
561 script.WriteRawImage("/recovery", "recovery.img")
562 script.AppendExtra("""
563set_stage("%(bcb_dev)s", "3/3");
564reboot_now("%(bcb_dev)s", "recovery");
565else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
566""" % bcb_dev)
567
Doug Zongkere5ff5902012-01-17 10:55:37 -0800568 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700569
Doug Zongker01ce19c2014-02-04 13:48:15 -0800570 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700571
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700572 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800573 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700574 if HasVendorPartition(input_zip):
575 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700576
Kenny Rootf32dc712012-04-08 10:42:34 -0700577 if "selinux_fc" in OPTIONS.info_dict:
578 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500579
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700580 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700581 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800582 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700583 # Full OTA is done as an "incremental" against an empty source
584 # image. This has the effect of writing new data from the package
585 # to the entire partition, but lets us reuse the updater code that
586 # writes incrementals to do it.
587 system_tgt = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
588 system_tgt.ResetFileMap()
589 system_diff = BlockDifference("system", system_tgt, src=None)
590 system_diff.WriteScript(script, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800591 else:
592 script.FormatPartition("/system")
593 script.Mount("/system")
594 if not has_recovery_patch:
595 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800596 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700597
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700598 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800599 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700600
Doug Zongker55d93282011-01-25 17:03:34 -0800601 boot_img = common.GetBootableImage("boot.img", "boot.img",
602 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800603
Doug Zongker91a99c22014-05-09 13:15:01 -0700604 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800605 def output_sink(fn, data):
606 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700607 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800608
609 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
610 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700611
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700612 system_items.GetMetadata(input_zip)
613 system_items.Get("system").SetPermissions(script)
614
615 if HasVendorPartition(input_zip):
616 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
617 script.ShowProgress(0.1, 0)
618
619 if block_based:
Doug Zongkerfc44a512014-08-26 13:10:25 -0700620 vendor_tgt = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
621 vendor_tgt.ResetFileMap()
622 vendor_diff = BlockDifference("vendor", vendor_tgt)
623 vendor_diff.WriteScript(script, output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700624 else:
625 script.FormatPartition("/vendor")
626 script.Mount("/vendor")
627 script.UnpackPackageDir("vendor", "/vendor")
628
629 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
630 script.MakeSymlinks(symlinks)
631
632 vendor_items.GetMetadata(input_zip)
633 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700634
Doug Zongker37974732010-09-16 17:44:38 -0700635 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700636 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700637
Doug Zongker01ce19c2014-02-04 13:48:15 -0800638 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700639 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700640
Doug Zongker01ce19c2014-02-04 13:48:15 -0800641 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700642 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700643
Doug Zongker1c390a22009-05-14 19:06:36 -0700644 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700645 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700646
Doug Zongker14833602010-02-02 13:12:04 -0800647 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800648
Doug Zongker922206e2014-03-04 13:16:24 -0800649 if OPTIONS.wipe_user_data:
650 script.ShowProgress(0.1, 10)
651 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700652
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800653 if OPTIONS.two_step:
654 script.AppendExtra("""
655set_stage("%(bcb_dev)s", "");
656""" % bcb_dev)
657 script.AppendExtra("else\n")
658 script.WriteRawImage("/boot", "recovery.img")
659 script.AppendExtra("""
660set_stage("%(bcb_dev)s", "2/3");
661reboot_now("%(bcb_dev)s", "");
662endif;
663endif;
664""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800665 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700666 WriteMetadata(metadata, output_zip)
667
Doug Zongkerfc44a512014-08-26 13:10:25 -0700668
Stephen Smalley56882bf2012-02-09 13:36:21 -0500669def WritePolicyConfig(file_context, output_zip):
670 f = open(file_context, 'r');
671 basename = os.path.basename(file_context)
672 common.ZipWriteStr(output_zip, basename, f.read())
673
Doug Zongker2ea21062010-04-28 16:05:21 -0700674
675def WriteMetadata(metadata, output_zip):
676 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
677 "".join(["%s=%s\n" % kv
678 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700679
Doug Zongkerfc44a512014-08-26 13:10:25 -0700680
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700681def LoadPartitionFiles(z, partition):
682 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700683 ZipFile, and return a dict of {filename: File object}."""
684 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700685 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700686 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700687 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700688 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700689 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700690 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700691 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800692 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700693
694
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700695def GetBuildProp(prop, info_dict):
696 """Return the fingerprint of the build of a given target-files info_dict."""
697 try:
698 return info_dict.get("build.prop", {})[prop]
699 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700700 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700701
Doug Zongkerfc44a512014-08-26 13:10:25 -0700702
Michael Runge4038aa82013-12-13 18:06:28 -0800703def AddToKnownPaths(filename, known_paths):
704 if filename[-1] == "/":
705 return
706 dirs = filename.split("/")[:-1]
707 while len(dirs) > 0:
708 path = "/".join(dirs)
709 if path in known_paths:
710 break;
711 known_paths.add(path)
712 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700713
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700714
Geremy Condra36bd3652014-02-06 19:45:10 -0800715def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
716 source_version = OPTIONS.source_info_dict["recovery_api_version"]
717 target_version = OPTIONS.target_info_dict["recovery_api_version"]
718
719 if source_version == 0:
720 print ("WARNING: generating edify script for a source that "
721 "can't install it.")
722 script = edify_generator.EdifyGenerator(source_version,
723 OPTIONS.target_info_dict)
724
725 metadata = {"pre-device": GetBuildProp("ro.product.device",
726 OPTIONS.source_info_dict),
727 "post-timestamp": GetBuildProp("ro.build.date.utc",
728 OPTIONS.target_info_dict),
729 }
730
731 device_specific = common.DeviceSpecificParams(
732 source_zip=source_zip,
733 source_version=source_version,
734 target_zip=target_zip,
735 target_version=target_version,
736 output_zip=output_zip,
737 script=script,
738 metadata=metadata,
739 info_dict=OPTIONS.info_dict)
740
741 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
742 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
743 metadata["pre-build"] = source_fp
744 metadata["post-build"] = target_fp
745
746 source_boot = common.GetBootableImage(
747 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
748 OPTIONS.source_info_dict)
749 target_boot = common.GetBootableImage(
750 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
751 updating_boot = (not OPTIONS.two_step and
752 (source_boot.data != target_boot.data))
753
754 source_recovery = common.GetBootableImage(
755 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
756 OPTIONS.source_info_dict)
757 target_recovery = common.GetBootableImage(
758 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
759 updating_recovery = (source_recovery.data != target_recovery.data)
760
Doug Zongkerfc44a512014-08-26 13:10:25 -0700761 system_src = GetImage("system", OPTIONS.source_tmp, OPTIONS.source_info_dict)
762 system_tgt = GetImage("system", OPTIONS.target_tmp, OPTIONS.target_info_dict)
763 system_diff = BlockDifference("system", system_tgt, system_src)
764
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700765 if HasVendorPartition(target_zip):
766 if not HasVendorPartition(source_zip):
767 raise RuntimeError("can't generate incremental that adds /vendor")
Doug Zongkerfc44a512014-08-26 13:10:25 -0700768 vendor_src = GetImage("vendor", OPTIONS.source_tmp, OPTIONS.source_info_dict)
769 vendor_tgt = GetImage("vendor", OPTIONS.target_tmp, OPTIONS.target_info_dict)
770 vendor_diff = BlockDifference("vendor", vendor_tgt, vendor_src)
771 else:
772 vendor_diff = None
Geremy Condra36bd3652014-02-06 19:45:10 -0800773
Michael Rungec6e3afd2014-05-05 11:55:47 -0700774 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
775 oem_dict = None
776 if oem_props is not None:
777 if OPTIONS.oem_source is None:
778 raise common.ExternalError("OEM source required for this build")
779 script.Mount("/oem")
780 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
781
782 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800783 device_specific.IncrementalOTA_Assertions()
784
785 # Two-step incremental package strategy (in chronological order,
786 # which is *not* the order in which the generated script has
787 # things):
788 #
789 # if stage is not "2/3" or "3/3":
790 # do verification on current system
791 # write recovery image to boot partition
792 # set stage to "2/3"
793 # reboot to boot partition and restart recovery
794 # else if stage is "2/3":
795 # write recovery image to recovery partition
796 # set stage to "3/3"
797 # reboot to recovery partition and restart recovery
798 # else:
799 # (stage must be "3/3")
800 # perform update:
801 # patch system files, etc.
802 # force full install of new boot image
803 # set up system to update recovery partition on first boot
804 # complete script normally (allow recovery to mark itself finished and reboot)
805
806 if OPTIONS.two_step:
807 if not OPTIONS.info_dict.get("multistage_support", None):
808 assert False, "two-step packages not supported by this build"
809 fs = OPTIONS.info_dict["fstab"]["/misc"]
810 assert fs.fs_type.upper() == "EMMC", \
811 "two-step packages only supported on devices with EMMC /misc partitions"
812 bcb_dev = {"bcb_dev": fs.device}
813 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
814 script.AppendExtra("""
815if get_stage("%(bcb_dev)s", "stage") == "2/3" then
816""" % bcb_dev)
817 script.AppendExtra("sleep(20);\n");
818 script.WriteRawImage("/recovery", "recovery.img")
819 script.AppendExtra("""
820set_stage("%(bcb_dev)s", "3/3");
821reboot_now("%(bcb_dev)s", "recovery");
822else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
823""" % bcb_dev)
824
825 script.Print("Verifying current system...")
826
827 device_specific.IncrementalOTA_VerifyBegin()
828
Michael Rungec6e3afd2014-05-05 11:55:47 -0700829 if oem_props is None:
830 script.AssertSomeFingerprint(source_fp, target_fp)
831 else:
832 script.AssertSomeThumbprint(
833 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
834 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800835
836 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700837 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800838 d = common.Difference(target_boot, source_boot)
839 _, _, d = d.ComputePatch()
Doug Zongkerf8340082014-08-05 10:39:37 -0700840 if d is None:
841 include_full_boot = True
842 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
843 else:
844 include_full_boot = False
Geremy Condra36bd3652014-02-06 19:45:10 -0800845
Doug Zongkerf8340082014-08-05 10:39:37 -0700846 print "boot target: %d source: %d diff: %d" % (
847 target_boot.size, source_boot.size, len(d))
Geremy Condra36bd3652014-02-06 19:45:10 -0800848
Doug Zongkerf8340082014-08-05 10:39:37 -0700849 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Geremy Condra36bd3652014-02-06 19:45:10 -0800850
Doug Zongkerf8340082014-08-05 10:39:37 -0700851 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
852 (boot_type, boot_device,
853 source_boot.size, source_boot.sha1,
854 target_boot.size, target_boot.sha1))
Geremy Condra36bd3652014-02-06 19:45:10 -0800855
856 device_specific.IncrementalOTA_VerifyEnd()
857
858 if OPTIONS.two_step:
859 script.WriteRawImage("/boot", "recovery.img")
860 script.AppendExtra("""
861set_stage("%(bcb_dev)s", "2/3");
862reboot_now("%(bcb_dev)s", "");
863else
864""" % bcb_dev)
865
866 script.Comment("---- start making changes here ----")
867
868 device_specific.IncrementalOTA_InstallBegin()
869
Doug Zongkerfc44a512014-08-26 13:10:25 -0700870 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' %
871 (system_diff.device, system_src.care_map.to_string_raw(),
872 system_src.TotalSha1()))
873 script.Print("Patching system image...")
874 script.ShowProgress(0.8 if vendor_diff else 0.9, 0)
875 system_diff.WriteScript(script, output_zip)
876 script.AppendExtra(('else\n'
877 ' (range_sha1("%s", "%s") == "%s") ||\n'
878 ' abort("system partition has unexpected contents");\n'
879 'endif;') %
880 (system_diff.device, system_tgt.care_map.to_string_raw(),
881 system_tgt.TotalSha1()))
882
883 if vendor_diff:
884 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' %
885 (vendor_diff.device, vendor_src.care_map.to_string_raw(),
886 vendor_src.TotalSha1()))
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700887 script.Print("Patching vendor image...")
888 script.ShowProgress(0.1, 0)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700889 vendor_diff.WriteScript(script, output_zip)
890 script.AppendExtra(('else\n'
891 ' (range_sha1("%s", "%s") == "%s") ||\n'
892 ' abort("vendor partition has unexpected contents");\n'
893 'endif;') %
894 (vendor_diff.device, vendor_tgt.care_map.to_string_raw(),
895 vendor_tgt.TotalSha1()))
Geremy Condra36bd3652014-02-06 19:45:10 -0800896
897 if OPTIONS.two_step:
898 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
899 script.WriteRawImage("/boot", "boot.img")
900 print "writing full boot image (forced by two-step mode)"
901
902 if not OPTIONS.two_step:
903 if updating_boot:
Doug Zongkerf8340082014-08-05 10:39:37 -0700904 if include_full_boot:
905 print "boot image changed; including full."
906 script.Print("Installing boot image...")
907 script.WriteRawImage("/boot", "boot.img")
908 else:
909 # Produce the boot image by applying a patch to the current
910 # contents of the boot partition, and write it back to the
911 # partition.
912 print "boot image changed; including patch."
913 script.Print("Patching boot image...")
914 script.ShowProgress(0.1, 10)
915 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
916 % (boot_type, boot_device,
917 source_boot.size, source_boot.sha1,
918 target_boot.size, target_boot.sha1),
919 "-",
920 target_boot.size, target_boot.sha1,
921 source_boot.sha1, "patch/boot.img.p")
Geremy Condra36bd3652014-02-06 19:45:10 -0800922 else:
923 print "boot image unchanged; skipping."
924
925 # Do device-specific installation (eg, write radio image).
926 device_specific.IncrementalOTA_InstallEnd()
927
928 if OPTIONS.extra_script is not None:
929 script.AppendExtra(OPTIONS.extra_script)
930
Doug Zongker922206e2014-03-04 13:16:24 -0800931 if OPTIONS.wipe_user_data:
932 script.Print("Erasing user data...")
933 script.FormatPartition("/data")
934
Geremy Condra36bd3652014-02-06 19:45:10 -0800935 if OPTIONS.two_step:
936 script.AppendExtra("""
937set_stage("%(bcb_dev)s", "");
938endif;
939endif;
940""" % bcb_dev)
941
942 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800943 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800944 WriteMetadata(metadata, output_zip)
945
Doug Zongker32b527d2014-03-04 10:03:02 -0800946
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700947class FileDifference:
948 def __init__(self, partition, source_zip, target_zip, output_zip):
949 print "Loading target..."
950 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
951 print "Loading source..."
952 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
953
954 self.verbatim_targets = verbatim_targets = []
955 self.patch_list = patch_list = []
956 diffs = []
957 self.renames = renames = {}
958 known_paths = set()
959 largest_source_size = 0
960
961 matching_file_cache = {}
962 for fn, sf in source_data.items():
963 assert fn == sf.name
964 matching_file_cache["path:" + fn] = sf
965 if fn in target_data.keys():
966 AddToKnownPaths(fn, known_paths)
967 # Only allow eligibility for filename/sha matching
968 # if there isn't a perfect path match.
969 if target_data.get(sf.name) is None:
970 matching_file_cache["file:" + fn.split("/")[-1]] = sf
971 matching_file_cache["sha:" + sf.sha1] = sf
972
973 for fn in sorted(target_data.keys()):
974 tf = target_data[fn]
975 assert fn == tf.name
976 sf = ClosestFileMatch(tf, matching_file_cache, renames)
977 if sf is not None and sf.name != tf.name:
978 print "File has moved from " + sf.name + " to " + tf.name
979 renames[sf.name] = tf
980
981 if sf is None or fn in OPTIONS.require_verbatim:
982 # This file should be included verbatim
983 if fn in OPTIONS.prohibit_verbatim:
984 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
985 print "send", fn, "verbatim"
986 tf.AddToZip(output_zip)
987 verbatim_targets.append((fn, tf.size))
988 if fn in target_data.keys():
989 AddToKnownPaths(fn, known_paths)
990 elif tf.sha1 != sf.sha1:
991 # File is different; consider sending as a patch
992 diffs.append(common.Difference(tf, sf))
993 else:
994 # Target file data identical to source (may still be renamed)
995 pass
996
997 common.ComputeDifferences(diffs)
998
999 for diff in diffs:
1000 tf, sf, d = diff.GetPatch()
1001 path = "/".join(tf.name.split("/")[:-1])
1002 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1003 path not in known_paths:
1004 # patch is almost as big as the file; don't bother patching
1005 # or a patch + rename cannot take place due to the target
1006 # directory not existing
1007 tf.AddToZip(output_zip)
1008 verbatim_targets.append((tf.name, tf.size))
1009 if sf.name in renames:
1010 del renames[sf.name]
1011 AddToKnownPaths(tf.name, known_paths)
1012 else:
1013 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1014 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1015 largest_source_size = max(largest_source_size, sf.size)
1016
1017 self.largest_source_size = largest_source_size
1018
1019 def EmitVerification(self, script):
1020 so_far = 0
1021 for tf, sf, size, patch_sha in self.patch_list:
1022 if tf.name != sf.name:
1023 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1024 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1025 so_far += sf.size
1026 return so_far
1027
1028 def RemoveUnneededFiles(self, script, extras=()):
1029 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
1030 ["/"+i for i in sorted(self.source_data)
1031 if i not in self.target_data and
1032 i not in self.renames] +
1033 list(extras))
1034
1035 def TotalPatchSize(self):
1036 return sum(i[1].size for i in self.patch_list)
1037
1038 def EmitPatches(self, script, total_patch_size, so_far):
1039 self.deferred_patch_list = deferred_patch_list = []
1040 for item in self.patch_list:
1041 tf, sf, size, _ = item
1042 if tf.name == "system/build.prop":
1043 deferred_patch_list.append(item)
1044 continue
1045 if (sf.name != tf.name):
1046 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1047 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1048 so_far += tf.size
1049 script.SetProgress(so_far / total_patch_size)
1050 return so_far
1051
1052 def EmitDeferredPatches(self, script):
1053 for item in self.deferred_patch_list:
1054 tf, sf, size, _ = item
1055 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1056 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1057
1058 def EmitRenames(self, script):
1059 if len(self.renames) > 0:
1060 script.Print("Renaming files...")
1061 for src, tgt in self.renames.iteritems():
1062 print "Renaming " + src + " to " + tgt.name
1063 script.RenameFile(src, tgt.name)
1064
1065
1066
1067
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001068def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001069 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1070 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1071
Doug Zongker26e66192014-02-20 13:22:07 -08001072 if (OPTIONS.block_based and
1073 target_has_recovery_patch and
1074 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001075 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1076
Doug Zongker37974732010-09-16 17:44:38 -07001077 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1078 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001079
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001080 if source_version == 0:
1081 print ("WARNING: generating edify script for a source that "
1082 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001083 script = edify_generator.EdifyGenerator(source_version,
1084 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001085
Michael Runge6e836112014-04-15 17:40:21 -07001086 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1087 oem_dict = None
1088 if oem_props is not None:
1089 if OPTIONS.oem_source is None:
1090 raise common.ExternalError("OEM source required for this build")
1091 script.Mount("/oem")
1092 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1093
1094 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001095 OPTIONS.source_info_dict),
1096 "post-timestamp": GetBuildProp("ro.build.date.utc",
1097 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001098 }
1099
Doug Zongker05d3dea2009-06-22 11:32:31 -07001100 device_specific = common.DeviceSpecificParams(
1101 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001102 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001103 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001104 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001105 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001106 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001107 metadata=metadata,
1108 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001109
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001110 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge6e836112014-04-15 17:40:21 -07001111 script.Mount("/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001112 if HasVendorPartition(target_zip):
1113 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1114 script.Mount("/vendor")
1115 else:
1116 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001117
1118 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1119 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1120
1121 if oem_props is None:
1122 script.AssertSomeFingerprint(source_fp, target_fp)
1123 else:
1124 script.AssertSomeThumbprint(
1125 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1126 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1127
Doug Zongker2ea21062010-04-28 16:05:21 -07001128 metadata["pre-build"] = source_fp
1129 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001130
Doug Zongker55d93282011-01-25 17:03:34 -08001131 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001132 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1133 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001134 target_boot = common.GetBootableImage(
1135 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001136 updating_boot = (not OPTIONS.two_step and
1137 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001138
Doug Zongker55d93282011-01-25 17:03:34 -08001139 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001140 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1141 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001142 target_recovery = common.GetBootableImage(
1143 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001144 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001145
Doug Zongker881dd402009-09-20 14:03:55 -07001146 # Here's how we divide up the progress bar:
1147 # 0.1 for verifying the start state (PatchCheck calls)
1148 # 0.8 for applying patches (ApplyPatch calls)
1149 # 0.1 for unpacking verbatim files, symlinking, and doing the
1150 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001151
Michael Runge6e836112014-04-15 17:40:21 -07001152 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001153 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001154
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001155 # Two-step incremental package strategy (in chronological order,
1156 # which is *not* the order in which the generated script has
1157 # things):
1158 #
1159 # if stage is not "2/3" or "3/3":
1160 # do verification on current system
1161 # write recovery image to boot partition
1162 # set stage to "2/3"
1163 # reboot to boot partition and restart recovery
1164 # else if stage is "2/3":
1165 # write recovery image to recovery partition
1166 # set stage to "3/3"
1167 # reboot to recovery partition and restart recovery
1168 # else:
1169 # (stage must be "3/3")
1170 # perform update:
1171 # patch system files, etc.
1172 # force full install of new boot image
1173 # set up system to update recovery partition on first boot
1174 # complete script normally (allow recovery to mark itself finished and reboot)
1175
1176 if OPTIONS.two_step:
1177 if not OPTIONS.info_dict.get("multistage_support", None):
1178 assert False, "two-step packages not supported by this build"
1179 fs = OPTIONS.info_dict["fstab"]["/misc"]
1180 assert fs.fs_type.upper() == "EMMC", \
1181 "two-step packages only supported on devices with EMMC /misc partitions"
1182 bcb_dev = {"bcb_dev": fs.device}
1183 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1184 script.AppendExtra("""
1185if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1186""" % bcb_dev)
1187 script.AppendExtra("sleep(20);\n");
1188 script.WriteRawImage("/recovery", "recovery.img")
1189 script.AppendExtra("""
1190set_stage("%(bcb_dev)s", "3/3");
1191reboot_now("%(bcb_dev)s", "recovery");
1192else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1193""" % bcb_dev)
1194
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001195 script.Print("Verifying current system...")
1196
Doug Zongkere5ff5902012-01-17 10:55:37 -08001197 device_specific.IncrementalOTA_VerifyBegin()
1198
Doug Zongker881dd402009-09-20 14:03:55 -07001199 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001200 so_far = system_diff.EmitVerification(script)
1201 if vendor_diff:
1202 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001203
Doug Zongker5da317e2009-06-02 13:38:17 -07001204 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001205 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001206 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001207 print "boot target: %d source: %d diff: %d" % (
1208 target_boot.size, source_boot.size, len(d))
1209
Doug Zongker048e7ca2009-06-15 14:31:53 -07001210 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001211
Doug Zongker96a57e72010-09-26 14:57:41 -07001212 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001213
1214 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1215 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001216 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001217 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001218 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001219
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001220 size = []
1221 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1222 if vendor_diff:
1223 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1224 if size or updating_recovery or updating_boot:
1225 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001226
Doug Zongker05d3dea2009-06-22 11:32:31 -07001227 device_specific.IncrementalOTA_VerifyEnd()
1228
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001229 if OPTIONS.two_step:
1230 script.WriteRawImage("/boot", "recovery.img")
1231 script.AppendExtra("""
1232set_stage("%(bcb_dev)s", "2/3");
1233reboot_now("%(bcb_dev)s", "");
1234else
1235""" % bcb_dev)
1236
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001237 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001238
Doug Zongkere5ff5902012-01-17 10:55:37 -08001239 device_specific.IncrementalOTA_InstallBegin()
1240
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001241 if OPTIONS.two_step:
1242 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1243 script.WriteRawImage("/boot", "boot.img")
1244 print "writing full boot image (forced by two-step mode)"
1245
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001246 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001247 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1248 if vendor_diff:
1249 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001250
Doug Zongker881dd402009-09-20 14:03:55 -07001251 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001252 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1253 if vendor_diff:
1254 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001255 if updating_boot:
1256 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001257
1258 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001259 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1260 if vendor_diff:
1261 script.Print("Patching vendor files...")
1262 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001263
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001264 if not OPTIONS.two_step:
1265 if updating_boot:
1266 # Produce the boot image by applying a patch to the current
1267 # contents of the boot partition, and write it back to the
1268 # partition.
1269 script.Print("Patching boot image...")
1270 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1271 % (boot_type, boot_device,
1272 source_boot.size, source_boot.sha1,
1273 target_boot.size, target_boot.sha1),
1274 "-",
1275 target_boot.size, target_boot.sha1,
1276 source_boot.sha1, "patch/boot.img.p")
1277 so_far += target_boot.size
1278 script.SetProgress(so_far / total_patch_size)
1279 print "boot image changed; including."
1280 else:
1281 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001282
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001283 system_items = ItemSet("system", "META/filesystem_config.txt")
1284 if vendor_diff:
1285 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1286
Doug Zongkereef39442009-04-02 12:14:19 -07001287 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001288 # Recovery is generated as a patch using both the boot image
1289 # (which contains the same linux kernel as recovery) and the file
1290 # /system/etc/recovery-resource.dat (which contains all the images
1291 # used in the recovery UI) as sources. This lets us minimize the
1292 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001293 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001294 # For older builds where recovery-resource.dat is not present, we
1295 # use only the boot image as the source.
1296
Doug Zongkerc9253822014-02-04 12:17:58 -08001297 if not target_has_recovery_patch:
1298 def output_sink(fn, data):
1299 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001300 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001301
1302 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1303 target_recovery, target_boot)
1304 script.DeleteFiles(["/system/recovery-from-boot.p",
1305 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001306 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001307 else:
1308 print "recovery image unchanged; skipping."
1309
Doug Zongker881dd402009-09-20 14:03:55 -07001310 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001311
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001312 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1313 if vendor_diff:
1314 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1315
1316 temp_script = script.MakeTemporary()
1317 system_items.GetMetadata(target_zip)
1318 system_items.Get("system").SetPermissions(temp_script)
1319 if vendor_diff:
1320 vendor_items.GetMetadata(target_zip)
1321 vendor_items.Get("vendor").SetPermissions(temp_script)
1322
1323 # Note that this call will mess up the trees of Items, so make sure
1324 # we're done with them.
1325 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1326 if vendor_diff:
1327 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001328
1329 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001330 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1331
1332 # Delete all the symlinks in source that aren't in target. This
1333 # needs to happen before verbatim files are unpacked, in case a
1334 # symlink in the source is replaced by a real file in the target.
1335 to_delete = []
1336 for dest, link in source_symlinks:
1337 if link not in target_symlinks_d:
1338 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001339 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001340
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001341 if system_diff.verbatim_targets:
1342 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001343 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001344 if vendor_diff and vendor_diff.verbatim_targets:
1345 script.Print("Unpacking new vendor files...")
1346 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001347
Doug Zongkerc9253822014-02-04 12:17:58 -08001348 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001349 script.Print("Unpacking new recovery...")
1350 script.UnpackPackageDir("recovery", "/system")
1351
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001352 system_diff.EmitRenames(script)
1353 if vendor_diff:
1354 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001355
Doug Zongker05d3dea2009-06-22 11:32:31 -07001356 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001357
1358 # Create all the symlinks that don't already exist, or point to
1359 # somewhere different than what we want. Delete each symlink before
1360 # creating it, since the 'symlink' command won't overwrite.
1361 to_create = []
1362 for dest, link in target_symlinks:
1363 if link in source_symlinks_d:
1364 if dest != source_symlinks_d[link]:
1365 to_create.append((dest, link))
1366 else:
1367 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001368 script.DeleteFiles([i[1] for i in to_create])
1369 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001370
1371 # Now that the symlinks are created, we can set all the
1372 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001373 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001374
Doug Zongker881dd402009-09-20 14:03:55 -07001375 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001376 device_specific.IncrementalOTA_InstallEnd()
1377
Doug Zongker1c390a22009-05-14 19:06:36 -07001378 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001379 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001380
Doug Zongkere92f15a2011-08-26 13:46:40 -07001381 # Patch the build.prop file last, so if something fails but the
1382 # device can still come up, it appears to be the old build and will
1383 # get set the OTA package again to retry.
1384 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001385 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001386
Doug Zongker922206e2014-03-04 13:16:24 -08001387 if OPTIONS.wipe_user_data:
1388 script.Print("Erasing user data...")
1389 script.FormatPartition("/data")
1390
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001391 if OPTIONS.two_step:
1392 script.AppendExtra("""
1393set_stage("%(bcb_dev)s", "");
1394endif;
1395endif;
1396""" % bcb_dev)
1397
Doug Zongker25568482014-03-03 10:21:27 -08001398 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001399 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001400
1401
1402def main(argv):
1403
1404 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001405 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001406 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001407 elif o in ("-k", "--package_key"):
1408 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001409 elif o in ("-i", "--incremental_from"):
1410 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001411 elif o in ("-w", "--wipe_user_data"):
1412 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001413 elif o in ("-n", "--no_prereq"):
1414 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001415 elif o in ("-o", "--oem_settings"):
1416 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001417 elif o in ("-e", "--extra_script"):
1418 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001419 elif o in ("-a", "--aslr_mode"):
1420 if a in ("on", "On", "true", "True", "yes", "Yes"):
1421 OPTIONS.aslr_mode = True
1422 else:
1423 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001424 elif o in ("-t", "--worker_threads"):
1425 if a.isdigit():
1426 OPTIONS.worker_threads = int(a)
1427 else:
1428 raise ValueError("Cannot parse value %r for option %r - only "
1429 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001430 elif o in ("-2", "--two_step"):
1431 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001432 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001433 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001434 elif o == "--block":
1435 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001436 elif o in ("-b", "--binary"):
1437 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001438 elif o in ("--no_fallback_to_full",):
1439 OPTIONS.fallback_to_full = False
Doug Zongkereef39442009-04-02 12:14:19 -07001440 else:
1441 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001442 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001443
1444 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001445 extra_opts="b:k:i:d:wne:t:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001446 extra_long_opts=["board_config=",
1447 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001448 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001449 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001450 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001451 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001452 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001453 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001454 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001455 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001456 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001457 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001458 "oem_settings=",
Doug Zongker62d4f182014-08-04 16:06:43 -07001459 "no_fallback_to_full",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001460 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001461 extra_option_handler=option_handler)
1462
1463 if len(args) != 2:
1464 common.Usage(__doc__)
1465 sys.exit(1)
1466
Doug Zongker1c390a22009-05-14 19:06:36 -07001467 if OPTIONS.extra_script is not None:
1468 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1469
Doug Zongkereef39442009-04-02 12:14:19 -07001470 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001471 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001472
Doug Zongkereef39442009-04-02 12:14:19 -07001473 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001474 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001475
1476 # If this image was originally labelled with SELinux contexts, make sure we
1477 # also apply the labels in our new image. During building, the "file_contexts"
1478 # is in the out/ directory tree, but for repacking from target-files.zip it's
1479 # in the root directory of the ramdisk.
1480 if "selinux_fc" in OPTIONS.info_dict:
1481 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1482 "file_contexts")
1483
Doug Zongker37974732010-09-16 17:44:38 -07001484 if OPTIONS.verbose:
1485 print "--- target info ---"
1486 common.DumpInfoDict(OPTIONS.info_dict)
1487
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001488 # If the caller explicitly specified the device-specific extensions
1489 # path via -s/--device_specific, use that. Otherwise, use
1490 # META/releasetools.py if it is present in the target target_files.
1491 # Otherwise, take the path of the file from 'tool_extensions' in the
1492 # info dict and look for that in the local filesystem, relative to
1493 # the current directory.
1494
Doug Zongker37974732010-09-16 17:44:38 -07001495 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001496 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1497 if os.path.exists(from_input):
1498 print "(using device-specific extensions from target_files)"
1499 OPTIONS.device_specific = from_input
1500 else:
1501 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1502
Doug Zongker37974732010-09-16 17:44:38 -07001503 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001504 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001505
Doug Zongker62d4f182014-08-04 16:06:43 -07001506 while True:
Doug Zongkereef39442009-04-02 12:14:19 -07001507
Doug Zongker62d4f182014-08-04 16:06:43 -07001508 if OPTIONS.no_signing:
1509 if os.path.exists(args[1]): os.unlink(args[1])
1510 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1511 else:
1512 temp_zip_file = tempfile.NamedTemporaryFile()
1513 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1514 compression=zipfile.ZIP_DEFLATED)
1515
1516 if OPTIONS.incremental_source is None:
1517 WriteFullOTAPackage(input_zip, output_zip)
1518 if OPTIONS.package_key is None:
1519 OPTIONS.package_key = OPTIONS.info_dict.get(
1520 "default_system_dev_certificate",
1521 "build/target/product/security/testkey")
1522 break
1523
1524 else:
1525 print "unzipping source target-files..."
1526 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
1527 OPTIONS.target_info_dict = OPTIONS.info_dict
1528 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1529 if "selinux_fc" in OPTIONS.source_info_dict:
1530 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1531 "file_contexts")
1532 if OPTIONS.package_key is None:
1533 OPTIONS.package_key = OPTIONS.source_info_dict.get(
1534 "default_system_dev_certificate",
1535 "build/target/product/security/testkey")
1536 if OPTIONS.verbose:
1537 print "--- source info ---"
1538 common.DumpInfoDict(OPTIONS.source_info_dict)
1539 try:
1540 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
1541 break
1542 except ValueError:
1543 if not OPTIONS.fallback_to_full: raise
1544 print "--- failed to build incremental; falling back to full ---"
1545 OPTIONS.incremental_source = None
1546 output_zip.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001547
1548 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001549
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001550 if not OPTIONS.no_signing:
1551 SignOutput(temp_zip_file.name, args[1])
1552 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001553
Doug Zongkereef39442009-04-02 12:14:19 -07001554 print "done."
1555
1556
1557if __name__ == '__main__':
1558 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001559 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001560 main(sys.argv[1:])
1561 except common.ExternalError, e:
1562 print
1563 print " ERROR: %s" % (e,)
1564 print
1565 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -07001566 finally:
1567 common.Cleanup()