blob: f848fd55b34e49a93c61f8014ed18c47cd89ceeb [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 Zongkereef39442009-04-02 12:14:19 -070088import os
89import re
Doug Zongkereef39442009-04-02 12:14:19 -070090import subprocess
91import tempfile
92import time
93import zipfile
94
davidcad0bb92011-03-15 14:21:38 +000095try:
96 from hashlib import sha1 as sha1
97except ImportError:
98 from sha import sha as sha1
99
Doug Zongkereef39442009-04-02 12:14:19 -0700100import common
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700101import edify_generator
Geremy Condra36bd3652014-02-06 19:45:10 -0800102import build_image
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 Zongker761e6422009-09-25 10:45:39 -0700114OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800115OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900116OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800117OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800118OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700119OPTIONS.oem_source = None
Doug Zongker62d4f182014-08-04 16:06:43 -0700120OPTIONS.fallback_to_full = True
Doug Zongkereef39442009-04-02 12:14:19 -0700121
122def MostPopularKey(d, default):
123 """Given a dict, return the key corresponding to the largest
124 value. Returns 'default' if the dict is empty."""
125 x = [(v, k) for (k, v) in d.iteritems()]
126 if not x: return default
127 x.sort()
128 return x[-1][1]
129
130
131def IsSymlink(info):
132 """Return true if the zipfile.ZipInfo object passed in represents a
133 symlink."""
134 return (info.external_attr >> 16) == 0120777
135
Hristo Bojinov96be7202010-08-02 10:26:17 -0700136def IsRegular(info):
137 """Return true if the zipfile.ZipInfo object passed in represents a
138 symlink."""
139 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700140
Michael Runge4038aa82013-12-13 18:06:28 -0800141def ClosestFileMatch(src, tgtfiles, existing):
142 """Returns the closest file match between a source file and list
143 of potential matches. The exact filename match is preferred,
144 then the sha1 is searched for, and finally a file with the same
145 basename is evaluated. Rename support in the updater-binary is
146 required for the latter checks to be used."""
147
148 result = tgtfiles.get("path:" + src.name)
149 if result is not None:
150 return result
151
152 if not OPTIONS.target_info_dict.get("update_rename_support", False):
153 return None
154
155 if src.size < 1000:
156 return None
157
158 result = tgtfiles.get("sha1:" + src.sha1)
159 if result is not None and existing.get(result.name) is None:
160 return result
161 result = tgtfiles.get("file:" + src.name.split("/")[-1])
162 if result is not None and existing.get(result.name) is None:
163 return result
164 return None
165
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700166class ItemSet:
167 def __init__(self, partition, fs_config):
168 self.partition = partition
169 self.fs_config = fs_config
170 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700171
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700172 def Get(self, name, dir=False):
173 if name not in self.ITEMS:
174 self.ITEMS[name] = Item(self, name, dir=dir)
175 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700176
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700177 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700178 # The target_files contains a record of what the uid,
179 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700180 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700181
182 for line in output.split("\n"):
183 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700184 columns = line.split()
185 name, uid, gid, mode = columns[:4]
186 selabel = None
187 capabilities = None
188
189 # After the first 4 columns, there are a series of key=value
190 # pairs. Extract out the fields we care about.
191 for element in columns[4:]:
192 key, value = element.split("=")
193 if key == "selabel":
194 selabel = value
195 if key == "capabilities":
196 capabilities = value
197
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700198 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700199 if i is not None:
200 i.uid = int(uid)
201 i.gid = int(gid)
202 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700203 i.selabel = selabel
204 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700205 if i.dir:
206 i.children.sort(key=lambda i: i.name)
207
208 # set metadata for the files generated by this script.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700209 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700210 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700211 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700212 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700213
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700214
215class Item:
216 """Items represent the metadata (user, group, mode) of files and
217 directories in the system image."""
218 def __init__(self, itemset, name, dir=False):
219 self.itemset = itemset
220 self.name = name
221 self.uid = None
222 self.gid = None
223 self.mode = None
224 self.selabel = None
225 self.capabilities = None
226 self.dir = dir
227
228 if name:
229 self.parent = itemset.Get(os.path.dirname(name), dir=True)
230 self.parent.children.append(self)
231 else:
232 self.parent = None
233 if dir:
234 self.children = []
235
236 def Dump(self, indent=0):
237 if self.uid is not None:
238 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
239 else:
240 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
241 if self.dir:
242 print "%s%s" % (" "*indent, self.descendants)
243 print "%s%s" % (" "*indent, self.best_subtree)
244 for i in self.children:
245 i.Dump(indent=indent+1)
246
Doug Zongkereef39442009-04-02 12:14:19 -0700247 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700248 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
249 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700250 set_perm to correctly chown/chmod all the files to their desired
251 values. Recursively calls itself for all descendants.
252
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700253 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700254 all descendants of this node. (dmode or fmode may be None.) Also
255 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700256 dmode, fmode, selabel, capabilities) tuple that will match the most
257 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700258 """
259
260 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700261 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700262 for i in self.children:
263 if i.dir:
264 for k, v in i.CountChildMetadata().iteritems():
265 d[k] = d.get(k, 0) + v
266 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700267 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700268 d[k] = d.get(k, 0) + 1
269
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700270 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
271 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700272
273 # First, find the (uid, gid) pair that matches the most
274 # descendants.
275 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700276 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700277 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
278 ug = MostPopularKey(ug, (0, 0))
279
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700280 # Now find the dmode, fmode, selabel, and capabilities that match
281 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700282 best_dmode = (0, 0755)
283 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700284 best_selabel = (0, None)
285 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700286 for k, count in d.iteritems():
287 if k[:2] != ug: continue
288 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
289 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700290 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
291 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
292 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700293
294 return d
295
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700296 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700297 """Append set_perm/set_perm_recursive commands to 'script' to
298 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700299 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700300
301 self.CountChildMetadata()
302
303 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700304 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700305 # item (and all its children) have already been set to. We only
306 # need to issue set_perm/set_perm_recursive commands if we're
307 # supposed to be something different.
308 if item.dir:
309 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700310 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700311 current = item.best_subtree
312
313 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700314 item.mode != current[2] or item.selabel != current[4] or \
315 item.capabilities != current[5]:
316 script.SetPermissions("/"+item.name, item.uid, item.gid,
317 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700318
319 for i in item.children:
320 recurse(i, current)
321 else:
322 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700323 item.mode != current[3] or item.selabel != current[4] or \
324 item.capabilities != current[5]:
325 script.SetPermissions("/"+item.name, item.uid, item.gid,
326 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700327
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700328 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700329
330
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700331def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
332 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700333 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800334 list of symlinks. output_zip may be None, in which case the copy is
335 skipped (but the other side effects still happen). substitute is an
336 optional dict of {output filename: contents} to be output instead of
337 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700338 """
339
340 symlinks = []
341
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700342 partition = itemset.partition
343
Doug Zongkereef39442009-04-02 12:14:19 -0700344 for info in input_zip.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700345 if info.filename.startswith(partition.upper() + "/"):
Doug Zongkereef39442009-04-02 12:14:19 -0700346 basefilename = info.filename[7:]
347 if IsSymlink(info):
348 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700349 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700350 else:
351 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700352 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700353 if substitute and fn in substitute and substitute[fn] is None:
354 continue
355 if output_zip is not None:
356 if substitute and fn in substitute:
357 data = substitute[fn]
358 else:
359 data = input_zip.read(info.filename)
360 output_zip.writestr(info2, data)
361 if fn.endswith("/"):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700362 itemset.Get(fn[:-1], dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700363 else:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700364 itemset.Get(fn, dir=False)
Doug Zongkereef39442009-04-02 12:14:19 -0700365
366 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800367 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700368
369
Doug Zongkereef39442009-04-02 12:14:19 -0700370def SignOutput(temp_zip_name, output_zip_name):
371 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
372 pw = key_passwords[OPTIONS.package_key]
373
Doug Zongker951495f2009-08-14 12:44:19 -0700374 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
375 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700376
377
Michael Rungec6e3afd2014-05-05 11:55:47 -0700378def AppendAssertions(script, info_dict, oem_dict = None):
Michael Runge6e836112014-04-15 17:40:21 -0700379 oem_props = info_dict.get("oem_fingerprint_properties")
380 if oem_props is None:
381 device = GetBuildProp("ro.product.device", info_dict)
382 script.AssertDevice(device)
383 else:
384 if oem_dict is None:
385 raise common.ExternalError("No OEM file provided to answer expected assertions")
386 for prop in oem_props.split():
387 if oem_dict.get(prop) is None:
388 raise common.ExternalError("The OEM file is missing the property %s" % prop)
389 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700390
Doug Zongkereef39442009-04-02 12:14:19 -0700391
Doug Zongkerc9253822014-02-04 12:17:58 -0800392def HasRecoveryPatch(target_files_zip):
393 try:
394 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
395 return True
396 except KeyError:
397 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700398
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700399def HasVendorPartition(target_files_zip):
400 try:
401 target_files_zip.getinfo("VENDOR/")
402 return True
403 except KeyError:
404 return False
405
Michael Runge6e836112014-04-15 17:40:21 -0700406def GetOemProperty(name, oem_props, oem_dict, info_dict):
407 if oem_props is not None and name in oem_props:
408 return oem_dict[name]
409 return GetBuildProp(name, info_dict)
410
411
412def CalculateFingerprint(oem_props, oem_dict, info_dict):
413 if oem_props is None:
414 return GetBuildProp("ro.build.fingerprint", info_dict)
415 return "%s/%s/%s:%s" % (
416 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
417 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
418 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
419 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700420
Doug Zongker3c84f562014-07-31 11:06:30 -0700421def GetImage(which, tmpdir, info_dict):
422 # Return (mapdata, data) for the given image. which should be
423 # "system" or "vendor".
424
425 assert which in ("system", "vendor")
426
427 path = os.path.join(tmpdir, "IMAGES", which + ".img")
428 if os.path.exists(path):
429 print "using %s.img from target-files" % (which,)
430
431 # This is a 'new' target-files, which already has the image in it.
432 # The image is a sparse image, though, so we need to unsparse it
433 # and extract the map data.
434
435 success, name = build_image.UnsparseImage(path, replace=False)
436 if not success:
437 assert False, "unsparsing " + which + ".img failed"
438
439 mmap = tempfile.NamedTemporaryFile()
440 mimg = tempfile.NamedTemporaryFile(delete=False)
441 success = build_image.MappedUnsparseImage(
442 path, name, mmap.name, mimg.name)
443 if not success:
444 assert False, "creating sparse map failed"
445 os.unlink(name)
446 name = mimg.name
447
448 with open(mmap.name) as f:
449 mapdata = f.read()
450
451 try:
452 with open(name) as f:
453 data = f.read()
454 finally:
455 os.unlink(name)
456
457 print "unsparsed data sha1 is " + sha1(data).hexdigest()
458 return mapdata, data
459
460 else:
461 print "building %s.img from target-files" % (which,)
462
463 # This is an 'old' target-files, which does not contain images
464 # already built. Build them.
465
466 import add_img_to_target_files
467 if which == "system":
468 mapdata, data = add_img_to_target_files.BuildSystem(
469 tmpdir, info_dict, sparse=False, map_file=True)
470 elif which == "vendor":
471 mapdata, data = add_img_to_target_files.BuildVendor(
472 tmpdir, info_dict, sparse=False, map_file=True)
473
474 print "built data sha1 is " + sha1(data).hexdigest()
475 return mapdata, data
476
477
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700478def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700479 # TODO: how to determine this? We don't know what version it will
480 # be installed on top of. For now, we expect the API just won't
481 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700482 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700483
Michael Runge6e836112014-04-15 17:40:21 -0700484 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
485 oem_dict = None
486 if oem_props is not None:
487 if OPTIONS.oem_source is None:
488 raise common.ExternalError("OEM source required for this build")
489 script.Mount("/oem")
490 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
491
492 metadata = {"post-build": CalculateFingerprint(
493 oem_props, oem_dict, OPTIONS.info_dict),
494 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700495 OPTIONS.info_dict),
496 "post-timestamp": GetBuildProp("ro.build.date.utc",
497 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700498 }
499
Doug Zongker05d3dea2009-06-22 11:32:31 -0700500 device_specific = common.DeviceSpecificParams(
501 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700502 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700503 output_zip=output_zip,
504 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700505 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700506 metadata=metadata,
507 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700508
Doug Zongkerc9253822014-02-04 12:17:58 -0800509 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800510 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800511
Doug Zongker962069c2009-04-23 11:41:58 -0700512 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700513 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700514 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
515 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700516
Michael Runge6e836112014-04-15 17:40:21 -0700517 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700518 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800519
520 # Two-step package strategy (in chronological order, which is *not*
521 # the order in which the generated script has things):
522 #
523 # if stage is not "2/3" or "3/3":
524 # write recovery image to boot partition
525 # set stage to "2/3"
526 # reboot to boot partition and restart recovery
527 # else if stage is "2/3":
528 # write recovery image to recovery partition
529 # set stage to "3/3"
530 # reboot to recovery partition and restart recovery
531 # else:
532 # (stage must be "3/3")
533 # set stage to ""
534 # do normal full package installation:
535 # wipe and install system, boot image, etc.
536 # set up system to update recovery partition on first boot
537 # complete script normally (allow recovery to mark itself finished and reboot)
538
539 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
540 OPTIONS.input_tmp, "RECOVERY")
541 if OPTIONS.two_step:
542 if not OPTIONS.info_dict.get("multistage_support", None):
543 assert False, "two-step packages not supported by this build"
544 fs = OPTIONS.info_dict["fstab"]["/misc"]
545 assert fs.fs_type.upper() == "EMMC", \
546 "two-step packages only supported on devices with EMMC /misc partitions"
547 bcb_dev = {"bcb_dev": fs.device}
548 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
549 script.AppendExtra("""
550if get_stage("%(bcb_dev)s", "stage") == "2/3" then
551""" % bcb_dev)
552 script.WriteRawImage("/recovery", "recovery.img")
553 script.AppendExtra("""
554set_stage("%(bcb_dev)s", "3/3");
555reboot_now("%(bcb_dev)s", "recovery");
556else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
557""" % bcb_dev)
558
Doug Zongkere5ff5902012-01-17 10:55:37 -0800559 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700560
Doug Zongker01ce19c2014-02-04 13:48:15 -0800561 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700562
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700563 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800564 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700565 if HasVendorPartition(input_zip):
566 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700567
Kenny Rootf32dc712012-04-08 10:42:34 -0700568 if "selinux_fc" in OPTIONS.info_dict:
569 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500570
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700571 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700572 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800573 if block_based:
Doug Zongker3c84f562014-07-31 11:06:30 -0700574 mapdata, data = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
Doug Zongker5fad2032014-02-24 08:13:45 -0800575
576 common.ZipWriteStr(output_zip, "system.map", mapdata)
577 common.ZipWriteStr(output_zip, "system.muimg", data)
578 script.WipeBlockDevice("/system")
579 script.WriteRawImage("/system", "system.muimg", mapfn="system.map")
Doug Zongker01ce19c2014-02-04 13:48:15 -0800580 else:
581 script.FormatPartition("/system")
582 script.Mount("/system")
583 if not has_recovery_patch:
584 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800585 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700586
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700587 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800588 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700589
Doug Zongker55d93282011-01-25 17:03:34 -0800590 boot_img = common.GetBootableImage("boot.img", "boot.img",
591 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800592
Doug Zongker91a99c22014-05-09 13:15:01 -0700593 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800594 def output_sink(fn, data):
595 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700596 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800597
598 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
599 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700600
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700601 system_items.GetMetadata(input_zip)
602 system_items.Get("system").SetPermissions(script)
603
604 if HasVendorPartition(input_zip):
605 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
606 script.ShowProgress(0.1, 0)
607
608 if block_based:
Doug Zongker3c84f562014-07-31 11:06:30 -0700609 mapdata, data = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700610
611 common.ZipWriteStr(output_zip, "vendor.map", mapdata)
612 common.ZipWriteStr(output_zip, "vendor.muimg", data)
613 script.WipeBlockDevice("/vendor")
614 script.WriteRawImage("/vendor", "vendor.muimg", mapfn="vendor.map")
615 else:
616 script.FormatPartition("/vendor")
617 script.Mount("/vendor")
618 script.UnpackPackageDir("vendor", "/vendor")
619
620 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
621 script.MakeSymlinks(symlinks)
622
623 vendor_items.GetMetadata(input_zip)
624 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700625
Doug Zongker37974732010-09-16 17:44:38 -0700626 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700627 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700628
Doug Zongker01ce19c2014-02-04 13:48:15 -0800629 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700630 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700631
Doug Zongker01ce19c2014-02-04 13:48:15 -0800632 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700633 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700634
Doug Zongker1c390a22009-05-14 19:06:36 -0700635 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700636 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700637
Doug Zongker14833602010-02-02 13:12:04 -0800638 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800639
Doug Zongker922206e2014-03-04 13:16:24 -0800640 if OPTIONS.wipe_user_data:
641 script.ShowProgress(0.1, 10)
642 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700643
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800644 if OPTIONS.two_step:
645 script.AppendExtra("""
646set_stage("%(bcb_dev)s", "");
647""" % bcb_dev)
648 script.AppendExtra("else\n")
649 script.WriteRawImage("/boot", "recovery.img")
650 script.AppendExtra("""
651set_stage("%(bcb_dev)s", "2/3");
652reboot_now("%(bcb_dev)s", "");
653endif;
654endif;
655""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800656 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700657 WriteMetadata(metadata, output_zip)
658
Stephen Smalley56882bf2012-02-09 13:36:21 -0500659def WritePolicyConfig(file_context, output_zip):
660 f = open(file_context, 'r');
661 basename = os.path.basename(file_context)
662 common.ZipWriteStr(output_zip, basename, f.read())
663
Doug Zongker2ea21062010-04-28 16:05:21 -0700664
665def WriteMetadata(metadata, output_zip):
666 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
667 "".join(["%s=%s\n" % kv
668 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700669
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700670def LoadPartitionFiles(z, partition):
671 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700672 ZipFile, and return a dict of {filename: File object}."""
673 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700674 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700675 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700676 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700677 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700678 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700679 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700680 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800681 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700682
683
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700684def GetBuildProp(prop, info_dict):
685 """Return the fingerprint of the build of a given target-files info_dict."""
686 try:
687 return info_dict.get("build.prop", {})[prop]
688 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700689 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700690
Michael Runge4038aa82013-12-13 18:06:28 -0800691def AddToKnownPaths(filename, known_paths):
692 if filename[-1] == "/":
693 return
694 dirs = filename.split("/")[:-1]
695 while len(dirs) > 0:
696 path = "/".join(dirs)
697 if path in known_paths:
698 break;
699 known_paths.add(path)
700 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700701
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700702class BlockDifference:
Doug Zongker3c84f562014-07-31 11:06:30 -0700703 def __init__(self, partition, output_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700704 with tempfile.NamedTemporaryFile() as src_file:
705 with tempfile.NamedTemporaryFile() as tgt_file:
706 print "building source " + partition + " image..."
707 src_file = tempfile.NamedTemporaryFile()
Doug Zongker3c84f562014-07-31 11:06:30 -0700708 src_mapdata, src_data = GetImage(partition,
709 OPTIONS.source_tmp,
710 OPTIONS.source_info_dict)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700711
712 self.src_sha1 = sha1(src_data).hexdigest()
713 print "source " + partition + " sha1:", self.src_sha1
714 src_file.write(src_data)
715
716 print "building target " + partition + " image..."
717 tgt_file = tempfile.NamedTemporaryFile()
Doug Zongker3c84f562014-07-31 11:06:30 -0700718 tgt_mapdata, tgt_data = GetImage(partition,
719 OPTIONS.target_tmp,
720 OPTIONS.target_info_dict)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700721 self.tgt_sha1 = sha1(tgt_data).hexdigest()
722 print "target " + partition + " sha1:", self.tgt_sha1
723 tgt_len = len(tgt_data)
724 tgt_file.write(tgt_data)
725
726 system_type, self.device = common.GetTypeAndDevice("/" + partition,
727 OPTIONS.info_dict)
728 self.patch = common.MakePartitionPatch(src_file, tgt_file, partition)
729
730 TestBlockPatch(src_data, src_mapdata, self.patch.data,
731 tgt_mapdata, self.tgt_sha1)
732 src_data = None
733 tgt_data = None
734
735 self.patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
736 self.src_mapfilename = self.patch.name + ".src.map"
737 common.ZipWriteStr(output_zip, self.src_mapfilename, src_mapdata)
738 self.tgt_mapfilename = self.patch.name + ".tgt.map"
739 common.ZipWriteStr(output_zip, self.tgt_mapfilename, tgt_mapdata)
740
Geremy Condra36bd3652014-02-06 19:45:10 -0800741def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
742 source_version = OPTIONS.source_info_dict["recovery_api_version"]
743 target_version = OPTIONS.target_info_dict["recovery_api_version"]
744
745 if source_version == 0:
746 print ("WARNING: generating edify script for a source that "
747 "can't install it.")
748 script = edify_generator.EdifyGenerator(source_version,
749 OPTIONS.target_info_dict)
750
751 metadata = {"pre-device": GetBuildProp("ro.product.device",
752 OPTIONS.source_info_dict),
753 "post-timestamp": GetBuildProp("ro.build.date.utc",
754 OPTIONS.target_info_dict),
755 }
756
757 device_specific = common.DeviceSpecificParams(
758 source_zip=source_zip,
759 source_version=source_version,
760 target_zip=target_zip,
761 target_version=target_version,
762 output_zip=output_zip,
763 script=script,
764 metadata=metadata,
765 info_dict=OPTIONS.info_dict)
766
767 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
768 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
769 metadata["pre-build"] = source_fp
770 metadata["post-build"] = target_fp
771
772 source_boot = common.GetBootableImage(
773 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
774 OPTIONS.source_info_dict)
775 target_boot = common.GetBootableImage(
776 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
777 updating_boot = (not OPTIONS.two_step and
778 (source_boot.data != target_boot.data))
779
780 source_recovery = common.GetBootableImage(
781 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
782 OPTIONS.source_info_dict)
783 target_recovery = common.GetBootableImage(
784 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
785 updating_recovery = (source_recovery.data != target_recovery.data)
786
Doug Zongker3c84f562014-07-31 11:06:30 -0700787 system_diff = BlockDifference("system", output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700788 if HasVendorPartition(target_zip):
789 if not HasVendorPartition(source_zip):
790 raise RuntimeError("can't generate incremental that adds /vendor")
Doug Zongker3c84f562014-07-31 11:06:30 -0700791 vendor_diff = BlockDifference("vendor", output_zip)
Geremy Condra36bd3652014-02-06 19:45:10 -0800792
Michael Rungec6e3afd2014-05-05 11:55:47 -0700793 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
794 oem_dict = None
795 if oem_props is not None:
796 if OPTIONS.oem_source is None:
797 raise common.ExternalError("OEM source required for this build")
798 script.Mount("/oem")
799 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
800
801 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800802 device_specific.IncrementalOTA_Assertions()
803
804 # Two-step incremental package strategy (in chronological order,
805 # which is *not* the order in which the generated script has
806 # things):
807 #
808 # if stage is not "2/3" or "3/3":
809 # do verification on current system
810 # write recovery image to boot partition
811 # set stage to "2/3"
812 # reboot to boot partition and restart recovery
813 # else if stage is "2/3":
814 # write recovery image to recovery partition
815 # set stage to "3/3"
816 # reboot to recovery partition and restart recovery
817 # else:
818 # (stage must be "3/3")
819 # perform update:
820 # patch system files, etc.
821 # force full install of new boot image
822 # set up system to update recovery partition on first boot
823 # complete script normally (allow recovery to mark itself finished and reboot)
824
825 if OPTIONS.two_step:
826 if not OPTIONS.info_dict.get("multistage_support", None):
827 assert False, "two-step packages not supported by this build"
828 fs = OPTIONS.info_dict["fstab"]["/misc"]
829 assert fs.fs_type.upper() == "EMMC", \
830 "two-step packages only supported on devices with EMMC /misc partitions"
831 bcb_dev = {"bcb_dev": fs.device}
832 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
833 script.AppendExtra("""
834if get_stage("%(bcb_dev)s", "stage") == "2/3" then
835""" % bcb_dev)
836 script.AppendExtra("sleep(20);\n");
837 script.WriteRawImage("/recovery", "recovery.img")
838 script.AppendExtra("""
839set_stage("%(bcb_dev)s", "3/3");
840reboot_now("%(bcb_dev)s", "recovery");
841else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
842""" % bcb_dev)
843
844 script.Print("Verifying current system...")
845
846 device_specific.IncrementalOTA_VerifyBegin()
847
Michael Rungec6e3afd2014-05-05 11:55:47 -0700848 if oem_props is None:
849 script.AssertSomeFingerprint(source_fp, target_fp)
850 else:
851 script.AssertSomeThumbprint(
852 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
853 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800854
855 if updating_boot:
Geremy Condra36bd3652014-02-06 19:45:10 -0800856 d = common.Difference(target_boot, source_boot)
857 _, _, d = d.ComputePatch()
858 print "boot target: %d source: %d diff: %d" % (
859 target_boot.size, source_boot.size, len(d))
860
861 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
862
863 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
864
865 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
866 (boot_type, boot_device,
867 source_boot.size, source_boot.sha1,
868 target_boot.size, target_boot.sha1))
869
870 device_specific.IncrementalOTA_VerifyEnd()
871
872 if OPTIONS.two_step:
873 script.WriteRawImage("/boot", "recovery.img")
874 script.AppendExtra("""
875set_stage("%(bcb_dev)s", "2/3");
876reboot_now("%(bcb_dev)s", "");
877else
878""" % bcb_dev)
879
880 script.Comment("---- start making changes here ----")
881
882 device_specific.IncrementalOTA_InstallBegin()
883
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700884 if HasVendorPartition(target_zip):
885 script.Print("Patching vendor image...")
886 script.ShowProgress(0.1, 0)
887 script.Syspatch(vendor_diff.device,
888 vendor_diff.tgt_mapfilename, vendor_diff.tgt_sha1,
889 vendor_diff.src_mapfilename, vendor_diff.src_sha1,
890 vendor_diff.patch.name)
891 sys_progress = 0.8
892 else:
893 sys_progress = 0.9
894
Geremy Condra36bd3652014-02-06 19:45:10 -0800895 script.Print("Patching system image...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700896 script.ShowProgress(sys_progress, 0)
897 script.Syspatch(system_diff.device,
898 system_diff.tgt_mapfilename, system_diff.tgt_sha1,
899 system_diff.src_mapfilename, system_diff.src_sha1,
900 system_diff.patch.name)
Geremy Condra36bd3652014-02-06 19:45:10 -0800901
902 if OPTIONS.two_step:
903 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
904 script.WriteRawImage("/boot", "boot.img")
905 print "writing full boot image (forced by two-step mode)"
906
907 if not OPTIONS.two_step:
908 if updating_boot:
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 script.Print("Patching boot image...")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700913 script.ShowProgress(0.1, 10)
Geremy Condra36bd3652014-02-06 19:45:10 -0800914 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
915 % (boot_type, boot_device,
916 source_boot.size, source_boot.sha1,
917 target_boot.size, target_boot.sha1),
918 "-",
919 target_boot.size, target_boot.sha1,
920 source_boot.sha1, "patch/boot.img.p")
921 print "boot image changed; including."
922 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 -0800946def ParseMap(map_str):
947 x = map_str.split()
948 assert int(x[0]) == 4096
949 assert int(x[1]) == len(x)-2
950 return int(x[0]), [int(i) for i in x[2:]]
951
952def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1):
953 src_blksize, src_regions = ParseMap(src_map)
954 tgt_blksize, tgt_regions = ParseMap(tgt_map)
955
956 with tempfile.NamedTemporaryFile() as src_file,\
957 tempfile.NamedTemporaryFile() as patch_file,\
Doug Zongker32b527d2014-03-04 10:03:02 -0800958 tempfile.NamedTemporaryFile() as src_map_file,\
959 tempfile.NamedTemporaryFile() as tgt_map_file:
960
961 src_total = sum(src_regions) * src_blksize
962 src_file.truncate(src_total)
963 p = 0
964 for i in range(0, len(src_regions), 2):
965 c, dc = src_regions[i:i+2]
966 src_file.write(src_muimg[p:(p+c*src_blksize)])
967 p += c*src_blksize
968 src_file.seek(dc*src_blksize, 1)
969 assert src_file.tell() == src_total
970
971 patch_file.write(patch_data)
972
Doug Zongker32b527d2014-03-04 10:03:02 -0800973 src_map_file.write(src_map)
974 tgt_map_file.write(tgt_map)
975
976 src_file.flush()
977 src_map_file.flush()
978 patch_file.flush()
Doug Zongker32b527d2014-03-04 10:03:02 -0800979 tgt_map_file.flush()
980
981 p = common.Run(["syspatch_host", src_file.name, src_map_file.name,
Doug Zongker1113e382014-06-13 10:38:32 -0700982 patch_file.name, src_file.name, tgt_map_file.name],
Doug Zongker32b527d2014-03-04 10:03:02 -0800983 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
984 stdoutdata, _ = p.communicate()
985 if p.returncode != 0:
986 print stdoutdata
987 raise ValueError("failed to reconstruct target system image from patch")
988
989 h = sha1()
Doug Zongker1113e382014-06-13 10:38:32 -0700990 src_file.seek(0, 0)
Doug Zongker32b527d2014-03-04 10:03:02 -0800991 for i in range(0, len(tgt_regions), 2):
992 c, dc = tgt_regions[i:i+2]
Doug Zongker1113e382014-06-13 10:38:32 -0700993 h.update(src_file.read(c*tgt_blksize))
994 src_file.seek(dc*tgt_blksize, 1)
Doug Zongker32b527d2014-03-04 10:03:02 -0800995
996 if h.hexdigest() != tgt_sha1:
997 raise ValueError("patch reconstructed incorrect target system image")
998
999 print "test of system image patch succeeded"
1000
1001
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001002class FileDifference:
1003 def __init__(self, partition, source_zip, target_zip, output_zip):
1004 print "Loading target..."
1005 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1006 print "Loading source..."
1007 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1008
1009 self.verbatim_targets = verbatim_targets = []
1010 self.patch_list = patch_list = []
1011 diffs = []
1012 self.renames = renames = {}
1013 known_paths = set()
1014 largest_source_size = 0
1015
1016 matching_file_cache = {}
1017 for fn, sf in source_data.items():
1018 assert fn == sf.name
1019 matching_file_cache["path:" + fn] = sf
1020 if fn in target_data.keys():
1021 AddToKnownPaths(fn, known_paths)
1022 # Only allow eligibility for filename/sha matching
1023 # if there isn't a perfect path match.
1024 if target_data.get(sf.name) is None:
1025 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1026 matching_file_cache["sha:" + sf.sha1] = sf
1027
1028 for fn in sorted(target_data.keys()):
1029 tf = target_data[fn]
1030 assert fn == tf.name
1031 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1032 if sf is not None and sf.name != tf.name:
1033 print "File has moved from " + sf.name + " to " + tf.name
1034 renames[sf.name] = tf
1035
1036 if sf is None or fn in OPTIONS.require_verbatim:
1037 # This file should be included verbatim
1038 if fn in OPTIONS.prohibit_verbatim:
1039 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1040 print "send", fn, "verbatim"
1041 tf.AddToZip(output_zip)
1042 verbatim_targets.append((fn, tf.size))
1043 if fn in target_data.keys():
1044 AddToKnownPaths(fn, known_paths)
1045 elif tf.sha1 != sf.sha1:
1046 # File is different; consider sending as a patch
1047 diffs.append(common.Difference(tf, sf))
1048 else:
1049 # Target file data identical to source (may still be renamed)
1050 pass
1051
1052 common.ComputeDifferences(diffs)
1053
1054 for diff in diffs:
1055 tf, sf, d = diff.GetPatch()
1056 path = "/".join(tf.name.split("/")[:-1])
1057 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1058 path not in known_paths:
1059 # patch is almost as big as the file; don't bother patching
1060 # or a patch + rename cannot take place due to the target
1061 # directory not existing
1062 tf.AddToZip(output_zip)
1063 verbatim_targets.append((tf.name, tf.size))
1064 if sf.name in renames:
1065 del renames[sf.name]
1066 AddToKnownPaths(tf.name, known_paths)
1067 else:
1068 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1069 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1070 largest_source_size = max(largest_source_size, sf.size)
1071
1072 self.largest_source_size = largest_source_size
1073
1074 def EmitVerification(self, script):
1075 so_far = 0
1076 for tf, sf, size, patch_sha in self.patch_list:
1077 if tf.name != sf.name:
1078 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1079 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1080 so_far += sf.size
1081 return so_far
1082
1083 def RemoveUnneededFiles(self, script, extras=()):
1084 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
1085 ["/"+i for i in sorted(self.source_data)
1086 if i not in self.target_data and
1087 i not in self.renames] +
1088 list(extras))
1089
1090 def TotalPatchSize(self):
1091 return sum(i[1].size for i in self.patch_list)
1092
1093 def EmitPatches(self, script, total_patch_size, so_far):
1094 self.deferred_patch_list = deferred_patch_list = []
1095 for item in self.patch_list:
1096 tf, sf, size, _ = item
1097 if tf.name == "system/build.prop":
1098 deferred_patch_list.append(item)
1099 continue
1100 if (sf.name != tf.name):
1101 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1102 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1103 so_far += tf.size
1104 script.SetProgress(so_far / total_patch_size)
1105 return so_far
1106
1107 def EmitDeferredPatches(self, script):
1108 for item in self.deferred_patch_list:
1109 tf, sf, size, _ = item
1110 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1111 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1112
1113 def EmitRenames(self, script):
1114 if len(self.renames) > 0:
1115 script.Print("Renaming files...")
1116 for src, tgt in self.renames.iteritems():
1117 print "Renaming " + src + " to " + tgt.name
1118 script.RenameFile(src, tgt.name)
1119
1120
1121
1122
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001123def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001124 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1125 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1126
Doug Zongker26e66192014-02-20 13:22:07 -08001127 if (OPTIONS.block_based and
1128 target_has_recovery_patch and
1129 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001130 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1131
Doug Zongker37974732010-09-16 17:44:38 -07001132 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1133 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001134
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001135 if source_version == 0:
1136 print ("WARNING: generating edify script for a source that "
1137 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001138 script = edify_generator.EdifyGenerator(source_version,
1139 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001140
Michael Runge6e836112014-04-15 17:40:21 -07001141 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1142 oem_dict = None
1143 if oem_props is not None:
1144 if OPTIONS.oem_source is None:
1145 raise common.ExternalError("OEM source required for this build")
1146 script.Mount("/oem")
1147 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1148
1149 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001150 OPTIONS.source_info_dict),
1151 "post-timestamp": GetBuildProp("ro.build.date.utc",
1152 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001153 }
1154
Doug Zongker05d3dea2009-06-22 11:32:31 -07001155 device_specific = common.DeviceSpecificParams(
1156 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001157 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001158 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001159 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001160 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001161 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001162 metadata=metadata,
1163 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001164
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001165 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge6e836112014-04-15 17:40:21 -07001166 script.Mount("/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001167 if HasVendorPartition(target_zip):
1168 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1169 script.Mount("/vendor")
1170 else:
1171 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001172
1173 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1174 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1175
1176 if oem_props is None:
1177 script.AssertSomeFingerprint(source_fp, target_fp)
1178 else:
1179 script.AssertSomeThumbprint(
1180 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1181 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1182
Doug Zongker2ea21062010-04-28 16:05:21 -07001183 metadata["pre-build"] = source_fp
1184 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001185
Doug Zongker55d93282011-01-25 17:03:34 -08001186 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001187 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1188 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001189 target_boot = common.GetBootableImage(
1190 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001191 updating_boot = (not OPTIONS.two_step and
1192 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001193
Doug Zongker55d93282011-01-25 17:03:34 -08001194 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001195 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1196 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001197 target_recovery = common.GetBootableImage(
1198 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001199 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001200
Doug Zongker881dd402009-09-20 14:03:55 -07001201 # Here's how we divide up the progress bar:
1202 # 0.1 for verifying the start state (PatchCheck calls)
1203 # 0.8 for applying patches (ApplyPatch calls)
1204 # 0.1 for unpacking verbatim files, symlinking, and doing the
1205 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001206
Michael Runge6e836112014-04-15 17:40:21 -07001207 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001208 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001209
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001210 # Two-step incremental package strategy (in chronological order,
1211 # which is *not* the order in which the generated script has
1212 # things):
1213 #
1214 # if stage is not "2/3" or "3/3":
1215 # do verification on current system
1216 # write recovery image to boot partition
1217 # set stage to "2/3"
1218 # reboot to boot partition and restart recovery
1219 # else if stage is "2/3":
1220 # write recovery image to recovery partition
1221 # set stage to "3/3"
1222 # reboot to recovery partition and restart recovery
1223 # else:
1224 # (stage must be "3/3")
1225 # perform update:
1226 # patch system files, etc.
1227 # force full install of new boot image
1228 # set up system to update recovery partition on first boot
1229 # complete script normally (allow recovery to mark itself finished and reboot)
1230
1231 if OPTIONS.two_step:
1232 if not OPTIONS.info_dict.get("multistage_support", None):
1233 assert False, "two-step packages not supported by this build"
1234 fs = OPTIONS.info_dict["fstab"]["/misc"]
1235 assert fs.fs_type.upper() == "EMMC", \
1236 "two-step packages only supported on devices with EMMC /misc partitions"
1237 bcb_dev = {"bcb_dev": fs.device}
1238 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1239 script.AppendExtra("""
1240if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1241""" % bcb_dev)
1242 script.AppendExtra("sleep(20);\n");
1243 script.WriteRawImage("/recovery", "recovery.img")
1244 script.AppendExtra("""
1245set_stage("%(bcb_dev)s", "3/3");
1246reboot_now("%(bcb_dev)s", "recovery");
1247else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1248""" % bcb_dev)
1249
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001250 script.Print("Verifying current system...")
1251
Doug Zongkere5ff5902012-01-17 10:55:37 -08001252 device_specific.IncrementalOTA_VerifyBegin()
1253
Doug Zongker881dd402009-09-20 14:03:55 -07001254 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001255 so_far = system_diff.EmitVerification(script)
1256 if vendor_diff:
1257 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001258
Doug Zongker5da317e2009-06-02 13:38:17 -07001259 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001260 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001261 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001262 print "boot target: %d source: %d diff: %d" % (
1263 target_boot.size, source_boot.size, len(d))
1264
Doug Zongker048e7ca2009-06-15 14:31:53 -07001265 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001266
Doug Zongker96a57e72010-09-26 14:57:41 -07001267 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001268
1269 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1270 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001271 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001272 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001273 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001274
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001275 size = []
1276 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1277 if vendor_diff:
1278 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1279 if size or updating_recovery or updating_boot:
1280 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001281
Doug Zongker05d3dea2009-06-22 11:32:31 -07001282 device_specific.IncrementalOTA_VerifyEnd()
1283
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001284 if OPTIONS.two_step:
1285 script.WriteRawImage("/boot", "recovery.img")
1286 script.AppendExtra("""
1287set_stage("%(bcb_dev)s", "2/3");
1288reboot_now("%(bcb_dev)s", "");
1289else
1290""" % bcb_dev)
1291
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001292 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001293
Doug Zongkere5ff5902012-01-17 10:55:37 -08001294 device_specific.IncrementalOTA_InstallBegin()
1295
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001296 if OPTIONS.two_step:
1297 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1298 script.WriteRawImage("/boot", "boot.img")
1299 print "writing full boot image (forced by two-step mode)"
1300
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001301 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001302 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1303 if vendor_diff:
1304 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001305
Doug Zongker881dd402009-09-20 14:03:55 -07001306 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001307 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1308 if vendor_diff:
1309 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001310 if updating_boot:
1311 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001312
1313 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001314 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1315 if vendor_diff:
1316 script.Print("Patching vendor files...")
1317 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001318
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001319 if not OPTIONS.two_step:
1320 if updating_boot:
1321 # Produce the boot image by applying a patch to the current
1322 # contents of the boot partition, and write it back to the
1323 # partition.
1324 script.Print("Patching boot image...")
1325 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1326 % (boot_type, boot_device,
1327 source_boot.size, source_boot.sha1,
1328 target_boot.size, target_boot.sha1),
1329 "-",
1330 target_boot.size, target_boot.sha1,
1331 source_boot.sha1, "patch/boot.img.p")
1332 so_far += target_boot.size
1333 script.SetProgress(so_far / total_patch_size)
1334 print "boot image changed; including."
1335 else:
1336 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001337
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001338 system_items = ItemSet("system", "META/filesystem_config.txt")
1339 if vendor_diff:
1340 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1341
Doug Zongkereef39442009-04-02 12:14:19 -07001342 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001343 # Recovery is generated as a patch using both the boot image
1344 # (which contains the same linux kernel as recovery) and the file
1345 # /system/etc/recovery-resource.dat (which contains all the images
1346 # used in the recovery UI) as sources. This lets us minimize the
1347 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001348 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001349 # For older builds where recovery-resource.dat is not present, we
1350 # use only the boot image as the source.
1351
Doug Zongkerc9253822014-02-04 12:17:58 -08001352 if not target_has_recovery_patch:
1353 def output_sink(fn, data):
1354 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001355 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001356
1357 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1358 target_recovery, target_boot)
1359 script.DeleteFiles(["/system/recovery-from-boot.p",
1360 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001361 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001362 else:
1363 print "recovery image unchanged; skipping."
1364
Doug Zongker881dd402009-09-20 14:03:55 -07001365 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001366
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001367 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1368 if vendor_diff:
1369 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1370
1371 temp_script = script.MakeTemporary()
1372 system_items.GetMetadata(target_zip)
1373 system_items.Get("system").SetPermissions(temp_script)
1374 if vendor_diff:
1375 vendor_items.GetMetadata(target_zip)
1376 vendor_items.Get("vendor").SetPermissions(temp_script)
1377
1378 # Note that this call will mess up the trees of Items, so make sure
1379 # we're done with them.
1380 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1381 if vendor_diff:
1382 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001383
1384 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001385 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1386
1387 # Delete all the symlinks in source that aren't in target. This
1388 # needs to happen before verbatim files are unpacked, in case a
1389 # symlink in the source is replaced by a real file in the target.
1390 to_delete = []
1391 for dest, link in source_symlinks:
1392 if link not in target_symlinks_d:
1393 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001394 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001395
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001396 if system_diff.verbatim_targets:
1397 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001398 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001399 if vendor_diff and vendor_diff.verbatim_targets:
1400 script.Print("Unpacking new vendor files...")
1401 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001402
Doug Zongkerc9253822014-02-04 12:17:58 -08001403 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001404 script.Print("Unpacking new recovery...")
1405 script.UnpackPackageDir("recovery", "/system")
1406
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001407 system_diff.EmitRenames(script)
1408 if vendor_diff:
1409 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001410
Doug Zongker05d3dea2009-06-22 11:32:31 -07001411 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001412
1413 # Create all the symlinks that don't already exist, or point to
1414 # somewhere different than what we want. Delete each symlink before
1415 # creating it, since the 'symlink' command won't overwrite.
1416 to_create = []
1417 for dest, link in target_symlinks:
1418 if link in source_symlinks_d:
1419 if dest != source_symlinks_d[link]:
1420 to_create.append((dest, link))
1421 else:
1422 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001423 script.DeleteFiles([i[1] for i in to_create])
1424 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001425
1426 # Now that the symlinks are created, we can set all the
1427 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001428 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001429
Doug Zongker881dd402009-09-20 14:03:55 -07001430 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001431 device_specific.IncrementalOTA_InstallEnd()
1432
Doug Zongker1c390a22009-05-14 19:06:36 -07001433 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001434 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001435
Doug Zongkere92f15a2011-08-26 13:46:40 -07001436 # Patch the build.prop file last, so if something fails but the
1437 # device can still come up, it appears to be the old build and will
1438 # get set the OTA package again to retry.
1439 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001440 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001441
Doug Zongker922206e2014-03-04 13:16:24 -08001442 if OPTIONS.wipe_user_data:
1443 script.Print("Erasing user data...")
1444 script.FormatPartition("/data")
1445
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001446 if OPTIONS.two_step:
1447 script.AppendExtra("""
1448set_stage("%(bcb_dev)s", "");
1449endif;
1450endif;
1451""" % bcb_dev)
1452
Doug Zongker25568482014-03-03 10:21:27 -08001453 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001454 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001455
1456
1457def main(argv):
1458
1459 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001460 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001461 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001462 elif o in ("-k", "--package_key"):
1463 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001464 elif o in ("-i", "--incremental_from"):
1465 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001466 elif o in ("-w", "--wipe_user_data"):
1467 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001468 elif o in ("-n", "--no_prereq"):
1469 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001470 elif o in ("-o", "--oem_settings"):
1471 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001472 elif o in ("-e", "--extra_script"):
1473 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001474 elif o in ("-a", "--aslr_mode"):
1475 if a in ("on", "On", "true", "True", "yes", "Yes"):
1476 OPTIONS.aslr_mode = True
1477 else:
1478 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001479 elif o in ("-t", "--worker_threads"):
1480 if a.isdigit():
1481 OPTIONS.worker_threads = int(a)
1482 else:
1483 raise ValueError("Cannot parse value %r for option %r - only "
1484 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001485 elif o in ("-2", "--two_step"):
1486 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001487 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001488 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001489 elif o == "--block":
1490 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001491 elif o in ("-b", "--binary"):
1492 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001493 elif o in ("--no_fallback_to_full",):
1494 OPTIONS.fallback_to_full = False
Doug Zongkereef39442009-04-02 12:14:19 -07001495 else:
1496 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001497 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001498
1499 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001500 extra_opts="b:k:i:d:wne:t:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001501 extra_long_opts=["board_config=",
1502 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001503 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001504 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001505 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001506 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001507 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001508 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001509 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001510 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001511 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001512 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001513 "oem_settings=",
Doug Zongker62d4f182014-08-04 16:06:43 -07001514 "no_fallback_to_full",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001515 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001516 extra_option_handler=option_handler)
1517
1518 if len(args) != 2:
1519 common.Usage(__doc__)
1520 sys.exit(1)
1521
Doug Zongker1c390a22009-05-14 19:06:36 -07001522 if OPTIONS.extra_script is not None:
1523 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1524
Doug Zongkereef39442009-04-02 12:14:19 -07001525 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001526 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001527
Doug Zongkereef39442009-04-02 12:14:19 -07001528 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001529 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001530
1531 # If this image was originally labelled with SELinux contexts, make sure we
1532 # also apply the labels in our new image. During building, the "file_contexts"
1533 # is in the out/ directory tree, but for repacking from target-files.zip it's
1534 # in the root directory of the ramdisk.
1535 if "selinux_fc" in OPTIONS.info_dict:
1536 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1537 "file_contexts")
1538
Doug Zongker37974732010-09-16 17:44:38 -07001539 if OPTIONS.verbose:
1540 print "--- target info ---"
1541 common.DumpInfoDict(OPTIONS.info_dict)
1542
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001543 # If the caller explicitly specified the device-specific extensions
1544 # path via -s/--device_specific, use that. Otherwise, use
1545 # META/releasetools.py if it is present in the target target_files.
1546 # Otherwise, take the path of the file from 'tool_extensions' in the
1547 # info dict and look for that in the local filesystem, relative to
1548 # the current directory.
1549
Doug Zongker37974732010-09-16 17:44:38 -07001550 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001551 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1552 if os.path.exists(from_input):
1553 print "(using device-specific extensions from target_files)"
1554 OPTIONS.device_specific = from_input
1555 else:
1556 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1557
Doug Zongker37974732010-09-16 17:44:38 -07001558 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001559 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001560
Doug Zongker62d4f182014-08-04 16:06:43 -07001561 while True:
Doug Zongkereef39442009-04-02 12:14:19 -07001562
Doug Zongker62d4f182014-08-04 16:06:43 -07001563 if OPTIONS.no_signing:
1564 if os.path.exists(args[1]): os.unlink(args[1])
1565 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1566 else:
1567 temp_zip_file = tempfile.NamedTemporaryFile()
1568 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1569 compression=zipfile.ZIP_DEFLATED)
1570
1571 if OPTIONS.incremental_source is None:
1572 WriteFullOTAPackage(input_zip, output_zip)
1573 if OPTIONS.package_key is None:
1574 OPTIONS.package_key = OPTIONS.info_dict.get(
1575 "default_system_dev_certificate",
1576 "build/target/product/security/testkey")
1577 break
1578
1579 else:
1580 print "unzipping source target-files..."
1581 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
1582 OPTIONS.target_info_dict = OPTIONS.info_dict
1583 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1584 if "selinux_fc" in OPTIONS.source_info_dict:
1585 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1586 "file_contexts")
1587 if OPTIONS.package_key is None:
1588 OPTIONS.package_key = OPTIONS.source_info_dict.get(
1589 "default_system_dev_certificate",
1590 "build/target/product/security/testkey")
1591 if OPTIONS.verbose:
1592 print "--- source info ---"
1593 common.DumpInfoDict(OPTIONS.source_info_dict)
1594 try:
1595 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
1596 break
1597 except ValueError:
1598 if not OPTIONS.fallback_to_full: raise
1599 print "--- failed to build incremental; falling back to full ---"
1600 OPTIONS.incremental_source = None
1601 output_zip.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001602
1603 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001604
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001605 if not OPTIONS.no_signing:
1606 SignOutput(temp_zip_file.name, args[1])
1607 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001608
1609 common.Cleanup()
1610
1611 print "done."
1612
1613
1614if __name__ == '__main__':
1615 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001616 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001617 main(sys.argv[1:])
1618 except common.ExternalError, e:
1619 print
1620 print " ERROR: %s" % (e,)
1621 print
1622 sys.exit(1)