blob: 54d4884456f99c7a38735e3053178c55feb9bd99 [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 Zongkereef39442009-04-02 12:14:19 -0700120
121def MostPopularKey(d, default):
122 """Given a dict, return the key corresponding to the largest
123 value. Returns 'default' if the dict is empty."""
124 x = [(v, k) for (k, v) in d.iteritems()]
125 if not x: return default
126 x.sort()
127 return x[-1][1]
128
129
130def IsSymlink(info):
131 """Return true if the zipfile.ZipInfo object passed in represents a
132 symlink."""
133 return (info.external_attr >> 16) == 0120777
134
Hristo Bojinov96be7202010-08-02 10:26:17 -0700135def IsRegular(info):
136 """Return true if the zipfile.ZipInfo object passed in represents a
137 symlink."""
138 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700139
Michael Runge4038aa82013-12-13 18:06:28 -0800140def ClosestFileMatch(src, tgtfiles, existing):
141 """Returns the closest file match between a source file and list
142 of potential matches. The exact filename match is preferred,
143 then the sha1 is searched for, and finally a file with the same
144 basename is evaluated. Rename support in the updater-binary is
145 required for the latter checks to be used."""
146
147 result = tgtfiles.get("path:" + src.name)
148 if result is not None:
149 return result
150
151 if not OPTIONS.target_info_dict.get("update_rename_support", False):
152 return None
153
154 if src.size < 1000:
155 return None
156
157 result = tgtfiles.get("sha1:" + src.sha1)
158 if result is not None and existing.get(result.name) is None:
159 return result
160 result = tgtfiles.get("file:" + src.name.split("/")[-1])
161 if result is not None and existing.get(result.name) is None:
162 return result
163 return None
164
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700165class ItemSet:
166 def __init__(self, partition, fs_config):
167 self.partition = partition
168 self.fs_config = fs_config
169 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700170
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700171 def Get(self, name, dir=False):
172 if name not in self.ITEMS:
173 self.ITEMS[name] = Item(self, name, dir=dir)
174 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700175
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700176 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700177 # The target_files contains a record of what the uid,
178 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700179 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700180
181 for line in output.split("\n"):
182 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700183 columns = line.split()
184 name, uid, gid, mode = columns[:4]
185 selabel = None
186 capabilities = None
187
188 # After the first 4 columns, there are a series of key=value
189 # pairs. Extract out the fields we care about.
190 for element in columns[4:]:
191 key, value = element.split("=")
192 if key == "selabel":
193 selabel = value
194 if key == "capabilities":
195 capabilities = value
196
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700197 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700198 if i is not None:
199 i.uid = int(uid)
200 i.gid = int(gid)
201 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700202 i.selabel = selabel
203 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700204 if i.dir:
205 i.children.sort(key=lambda i: i.name)
206
207 # set metadata for the files generated by this script.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700208 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700209 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700210 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700211 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700212
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700213
214class Item:
215 """Items represent the metadata (user, group, mode) of files and
216 directories in the system image."""
217 def __init__(self, itemset, name, dir=False):
218 self.itemset = itemset
219 self.name = name
220 self.uid = None
221 self.gid = None
222 self.mode = None
223 self.selabel = None
224 self.capabilities = None
225 self.dir = dir
226
227 if name:
228 self.parent = itemset.Get(os.path.dirname(name), dir=True)
229 self.parent.children.append(self)
230 else:
231 self.parent = None
232 if dir:
233 self.children = []
234
235 def Dump(self, indent=0):
236 if self.uid is not None:
237 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
238 else:
239 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
240 if self.dir:
241 print "%s%s" % (" "*indent, self.descendants)
242 print "%s%s" % (" "*indent, self.best_subtree)
243 for i in self.children:
244 i.Dump(indent=indent+1)
245
Doug Zongkereef39442009-04-02 12:14:19 -0700246 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700247 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
248 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700249 set_perm to correctly chown/chmod all the files to their desired
250 values. Recursively calls itself for all descendants.
251
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700252 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700253 all descendants of this node. (dmode or fmode may be None.) Also
254 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700255 dmode, fmode, selabel, capabilities) tuple that will match the most
256 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700257 """
258
259 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700260 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700261 for i in self.children:
262 if i.dir:
263 for k, v in i.CountChildMetadata().iteritems():
264 d[k] = d.get(k, 0) + v
265 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700266 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700267 d[k] = d.get(k, 0) + 1
268
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700269 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
270 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700271
272 # First, find the (uid, gid) pair that matches the most
273 # descendants.
274 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700275 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700276 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
277 ug = MostPopularKey(ug, (0, 0))
278
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700279 # Now find the dmode, fmode, selabel, and capabilities that match
280 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700281 best_dmode = (0, 0755)
282 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700283 best_selabel = (0, None)
284 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700285 for k, count in d.iteritems():
286 if k[:2] != ug: continue
287 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
288 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700289 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
290 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
291 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700292
293 return d
294
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700295 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700296 """Append set_perm/set_perm_recursive commands to 'script' to
297 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700298 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700299
300 self.CountChildMetadata()
301
302 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700303 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700304 # item (and all its children) have already been set to. We only
305 # need to issue set_perm/set_perm_recursive commands if we're
306 # supposed to be something different.
307 if item.dir:
308 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700309 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700310 current = item.best_subtree
311
312 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700313 item.mode != current[2] or item.selabel != current[4] or \
314 item.capabilities != current[5]:
315 script.SetPermissions("/"+item.name, item.uid, item.gid,
316 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700317
318 for i in item.children:
319 recurse(i, current)
320 else:
321 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700322 item.mode != current[3] or item.selabel != current[4] or \
323 item.capabilities != current[5]:
324 script.SetPermissions("/"+item.name, item.uid, item.gid,
325 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700326
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700327 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700328
329
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700330def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
331 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700332 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800333 list of symlinks. output_zip may be None, in which case the copy is
334 skipped (but the other side effects still happen). substitute is an
335 optional dict of {output filename: contents} to be output instead of
336 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700337 """
338
339 symlinks = []
340
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700341 partition = itemset.partition
342
Doug Zongkereef39442009-04-02 12:14:19 -0700343 for info in input_zip.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700344 if info.filename.startswith(partition.upper() + "/"):
Doug Zongkereef39442009-04-02 12:14:19 -0700345 basefilename = info.filename[7:]
346 if IsSymlink(info):
347 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700348 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700349 else:
350 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700351 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700352 if substitute and fn in substitute and substitute[fn] is None:
353 continue
354 if output_zip is not None:
355 if substitute and fn in substitute:
356 data = substitute[fn]
357 else:
358 data = input_zip.read(info.filename)
359 output_zip.writestr(info2, data)
360 if fn.endswith("/"):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700361 itemset.Get(fn[:-1], dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700362 else:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700363 itemset.Get(fn, dir=False)
Doug Zongkereef39442009-04-02 12:14:19 -0700364
365 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800366 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700367
368
Doug Zongkereef39442009-04-02 12:14:19 -0700369def SignOutput(temp_zip_name, output_zip_name):
370 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
371 pw = key_passwords[OPTIONS.package_key]
372
Doug Zongker951495f2009-08-14 12:44:19 -0700373 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
374 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700375
376
Michael Rungec6e3afd2014-05-05 11:55:47 -0700377def AppendAssertions(script, info_dict, oem_dict = None):
Michael Runge6e836112014-04-15 17:40:21 -0700378 oem_props = info_dict.get("oem_fingerprint_properties")
379 if oem_props is None:
380 device = GetBuildProp("ro.product.device", info_dict)
381 script.AssertDevice(device)
382 else:
383 if oem_dict is None:
384 raise common.ExternalError("No OEM file provided to answer expected assertions")
385 for prop in oem_props.split():
386 if oem_dict.get(prop) is None:
387 raise common.ExternalError("The OEM file is missing the property %s" % prop)
388 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700389
Doug Zongkereef39442009-04-02 12:14:19 -0700390
Doug Zongkerc9253822014-02-04 12:17:58 -0800391def HasRecoveryPatch(target_files_zip):
392 try:
393 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
394 return True
395 except KeyError:
396 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700397
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700398def HasVendorPartition(target_files_zip):
399 try:
400 target_files_zip.getinfo("VENDOR/")
401 return True
402 except KeyError:
403 return False
404
Michael Runge6e836112014-04-15 17:40:21 -0700405def GetOemProperty(name, oem_props, oem_dict, info_dict):
406 if oem_props is not None and name in oem_props:
407 return oem_dict[name]
408 return GetBuildProp(name, info_dict)
409
410
411def CalculateFingerprint(oem_props, oem_dict, info_dict):
412 if oem_props is None:
413 return GetBuildProp("ro.build.fingerprint", info_dict)
414 return "%s/%s/%s:%s" % (
415 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
416 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
417 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
418 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700419
Doug Zongker3c84f562014-07-31 11:06:30 -0700420def GetImage(which, tmpdir, info_dict):
421 # Return (mapdata, data) for the given image. which should be
422 # "system" or "vendor".
423
424 assert which in ("system", "vendor")
425
426 path = os.path.join(tmpdir, "IMAGES", which + ".img")
427 if os.path.exists(path):
428 print "using %s.img from target-files" % (which,)
429
430 # This is a 'new' target-files, which already has the image in it.
431 # The image is a sparse image, though, so we need to unsparse it
432 # and extract the map data.
433
434 success, name = build_image.UnsparseImage(path, replace=False)
435 if not success:
436 assert False, "unsparsing " + which + ".img failed"
437
438 mmap = tempfile.NamedTemporaryFile()
439 mimg = tempfile.NamedTemporaryFile(delete=False)
440 success = build_image.MappedUnsparseImage(
441 path, name, mmap.name, mimg.name)
442 if not success:
443 assert False, "creating sparse map failed"
444 os.unlink(name)
445 name = mimg.name
446
447 with open(mmap.name) as f:
448 mapdata = f.read()
449
450 try:
451 with open(name) as f:
452 data = f.read()
453 finally:
454 os.unlink(name)
455
456 print "unsparsed data sha1 is " + sha1(data).hexdigest()
457 return mapdata, data
458
459 else:
460 print "building %s.img from target-files" % (which,)
461
462 # This is an 'old' target-files, which does not contain images
463 # already built. Build them.
464
465 import add_img_to_target_files
466 if which == "system":
467 mapdata, data = add_img_to_target_files.BuildSystem(
468 tmpdir, info_dict, sparse=False, map_file=True)
469 elif which == "vendor":
470 mapdata, data = add_img_to_target_files.BuildVendor(
471 tmpdir, info_dict, sparse=False, map_file=True)
472
473 print "built data sha1 is " + sha1(data).hexdigest()
474 return mapdata, data
475
476
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700477def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700478 # TODO: how to determine this? We don't know what version it will
479 # be installed on top of. For now, we expect the API just won't
480 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700481 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700482
Michael Runge6e836112014-04-15 17:40:21 -0700483 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
484 oem_dict = None
485 if oem_props is not None:
486 if OPTIONS.oem_source is None:
487 raise common.ExternalError("OEM source required for this build")
488 script.Mount("/oem")
489 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
490
491 metadata = {"post-build": CalculateFingerprint(
492 oem_props, oem_dict, OPTIONS.info_dict),
493 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700494 OPTIONS.info_dict),
495 "post-timestamp": GetBuildProp("ro.build.date.utc",
496 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700497 }
498
Doug Zongker05d3dea2009-06-22 11:32:31 -0700499 device_specific = common.DeviceSpecificParams(
500 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700501 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700502 output_zip=output_zip,
503 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700504 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700505 metadata=metadata,
506 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700507
Doug Zongkerc9253822014-02-04 12:17:58 -0800508 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800509 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800510
Doug Zongker962069c2009-04-23 11:41:58 -0700511 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700512 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700513 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
514 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700515
Michael Runge6e836112014-04-15 17:40:21 -0700516 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700517 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800518
519 # Two-step package strategy (in chronological order, which is *not*
520 # the order in which the generated script has things):
521 #
522 # if stage is not "2/3" or "3/3":
523 # write recovery image to boot partition
524 # set stage to "2/3"
525 # reboot to boot partition and restart recovery
526 # else if stage is "2/3":
527 # write recovery image to recovery partition
528 # set stage to "3/3"
529 # reboot to recovery partition and restart recovery
530 # else:
531 # (stage must be "3/3")
532 # set stage to ""
533 # do normal full package installation:
534 # wipe and install system, boot image, etc.
535 # set up system to update recovery partition on first boot
536 # complete script normally (allow recovery to mark itself finished and reboot)
537
538 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
539 OPTIONS.input_tmp, "RECOVERY")
540 if OPTIONS.two_step:
541 if not OPTIONS.info_dict.get("multistage_support", None):
542 assert False, "two-step packages not supported by this build"
543 fs = OPTIONS.info_dict["fstab"]["/misc"]
544 assert fs.fs_type.upper() == "EMMC", \
545 "two-step packages only supported on devices with EMMC /misc partitions"
546 bcb_dev = {"bcb_dev": fs.device}
547 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
548 script.AppendExtra("""
549if get_stage("%(bcb_dev)s", "stage") == "2/3" then
550""" % bcb_dev)
551 script.WriteRawImage("/recovery", "recovery.img")
552 script.AppendExtra("""
553set_stage("%(bcb_dev)s", "3/3");
554reboot_now("%(bcb_dev)s", "recovery");
555else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
556""" % bcb_dev)
557
Doug Zongkere5ff5902012-01-17 10:55:37 -0800558 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700559
Doug Zongker01ce19c2014-02-04 13:48:15 -0800560 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700561
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700562 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800563 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700564 if HasVendorPartition(input_zip):
565 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700566
Kenny Rootf32dc712012-04-08 10:42:34 -0700567 if "selinux_fc" in OPTIONS.info_dict:
568 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500569
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700570 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700571 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800572 if block_based:
Doug Zongker3c84f562014-07-31 11:06:30 -0700573 mapdata, data = GetImage("system", OPTIONS.input_tmp, OPTIONS.info_dict)
Doug Zongker5fad2032014-02-24 08:13:45 -0800574
575 common.ZipWriteStr(output_zip, "system.map", mapdata)
576 common.ZipWriteStr(output_zip, "system.muimg", data)
577 script.WipeBlockDevice("/system")
578 script.WriteRawImage("/system", "system.muimg", mapfn="system.map")
Doug Zongker01ce19c2014-02-04 13:48:15 -0800579 else:
580 script.FormatPartition("/system")
581 script.Mount("/system")
582 if not has_recovery_patch:
583 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800584 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700585
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700586 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800587 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700588
Doug Zongker55d93282011-01-25 17:03:34 -0800589 boot_img = common.GetBootableImage("boot.img", "boot.img",
590 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800591
Doug Zongker91a99c22014-05-09 13:15:01 -0700592 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800593 def output_sink(fn, data):
594 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700595 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800596
597 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
598 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700599
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700600 system_items.GetMetadata(input_zip)
601 system_items.Get("system").SetPermissions(script)
602
603 if HasVendorPartition(input_zip):
604 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
605 script.ShowProgress(0.1, 0)
606
607 if block_based:
Doug Zongker3c84f562014-07-31 11:06:30 -0700608 mapdata, data = GetImage("vendor", OPTIONS.input_tmp, OPTIONS.info_dict)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700609
610 common.ZipWriteStr(output_zip, "vendor.map", mapdata)
611 common.ZipWriteStr(output_zip, "vendor.muimg", data)
612 script.WipeBlockDevice("/vendor")
613 script.WriteRawImage("/vendor", "vendor.muimg", mapfn="vendor.map")
614 else:
615 script.FormatPartition("/vendor")
616 script.Mount("/vendor")
617 script.UnpackPackageDir("vendor", "/vendor")
618
619 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
620 script.MakeSymlinks(symlinks)
621
622 vendor_items.GetMetadata(input_zip)
623 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700624
Doug Zongker37974732010-09-16 17:44:38 -0700625 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700626 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700627
Doug Zongker01ce19c2014-02-04 13:48:15 -0800628 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700629 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700630
Doug Zongker01ce19c2014-02-04 13:48:15 -0800631 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700632 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700633
Doug Zongker1c390a22009-05-14 19:06:36 -0700634 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700635 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700636
Doug Zongker14833602010-02-02 13:12:04 -0800637 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800638
Doug Zongker922206e2014-03-04 13:16:24 -0800639 if OPTIONS.wipe_user_data:
640 script.ShowProgress(0.1, 10)
641 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700642
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800643 if OPTIONS.two_step:
644 script.AppendExtra("""
645set_stage("%(bcb_dev)s", "");
646""" % bcb_dev)
647 script.AppendExtra("else\n")
648 script.WriteRawImage("/boot", "recovery.img")
649 script.AppendExtra("""
650set_stage("%(bcb_dev)s", "2/3");
651reboot_now("%(bcb_dev)s", "");
652endif;
653endif;
654""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800655 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700656 WriteMetadata(metadata, output_zip)
657
Stephen Smalley56882bf2012-02-09 13:36:21 -0500658def WritePolicyConfig(file_context, output_zip):
659 f = open(file_context, 'r');
660 basename = os.path.basename(file_context)
661 common.ZipWriteStr(output_zip, basename, f.read())
662
Doug Zongker2ea21062010-04-28 16:05:21 -0700663
664def WriteMetadata(metadata, output_zip):
665 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
666 "".join(["%s=%s\n" % kv
667 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700668
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700669def LoadPartitionFiles(z, partition):
670 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700671 ZipFile, and return a dict of {filename: File object}."""
672 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700673 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700674 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700675 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700676 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700677 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700678 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700679 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800680 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700681
682
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700683def GetBuildProp(prop, info_dict):
684 """Return the fingerprint of the build of a given target-files info_dict."""
685 try:
686 return info_dict.get("build.prop", {})[prop]
687 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700688 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700689
Michael Runge4038aa82013-12-13 18:06:28 -0800690def AddToKnownPaths(filename, known_paths):
691 if filename[-1] == "/":
692 return
693 dirs = filename.split("/")[:-1]
694 while len(dirs) > 0:
695 path = "/".join(dirs)
696 if path in known_paths:
697 break;
698 known_paths.add(path)
699 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700700
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700701class BlockDifference:
Doug Zongker3c84f562014-07-31 11:06:30 -0700702 def __init__(self, partition, output_zip):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700703 with tempfile.NamedTemporaryFile() as src_file:
704 with tempfile.NamedTemporaryFile() as tgt_file:
705 print "building source " + partition + " image..."
706 src_file = tempfile.NamedTemporaryFile()
Doug Zongker3c84f562014-07-31 11:06:30 -0700707 src_mapdata, src_data = GetImage(partition,
708 OPTIONS.source_tmp,
709 OPTIONS.source_info_dict)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700710
711 self.src_sha1 = sha1(src_data).hexdigest()
712 print "source " + partition + " sha1:", self.src_sha1
713 src_file.write(src_data)
714
715 print "building target " + partition + " image..."
716 tgt_file = tempfile.NamedTemporaryFile()
Doug Zongker3c84f562014-07-31 11:06:30 -0700717 tgt_mapdata, tgt_data = GetImage(partition,
718 OPTIONS.target_tmp,
719 OPTIONS.target_info_dict)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700720 self.tgt_sha1 = sha1(tgt_data).hexdigest()
721 print "target " + partition + " sha1:", self.tgt_sha1
722 tgt_len = len(tgt_data)
723 tgt_file.write(tgt_data)
724
725 system_type, self.device = common.GetTypeAndDevice("/" + partition,
726 OPTIONS.info_dict)
727 self.patch = common.MakePartitionPatch(src_file, tgt_file, partition)
728
729 TestBlockPatch(src_data, src_mapdata, self.patch.data,
730 tgt_mapdata, self.tgt_sha1)
731 src_data = None
732 tgt_data = None
733
734 self.patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
735 self.src_mapfilename = self.patch.name + ".src.map"
736 common.ZipWriteStr(output_zip, self.src_mapfilename, src_mapdata)
737 self.tgt_mapfilename = self.patch.name + ".tgt.map"
738 common.ZipWriteStr(output_zip, self.tgt_mapfilename, tgt_mapdata)
739
Geremy Condra36bd3652014-02-06 19:45:10 -0800740def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
741 source_version = OPTIONS.source_info_dict["recovery_api_version"]
742 target_version = OPTIONS.target_info_dict["recovery_api_version"]
743
744 if source_version == 0:
745 print ("WARNING: generating edify script for a source that "
746 "can't install it.")
747 script = edify_generator.EdifyGenerator(source_version,
748 OPTIONS.target_info_dict)
749
750 metadata = {"pre-device": GetBuildProp("ro.product.device",
751 OPTIONS.source_info_dict),
752 "post-timestamp": GetBuildProp("ro.build.date.utc",
753 OPTIONS.target_info_dict),
754 }
755
756 device_specific = common.DeviceSpecificParams(
757 source_zip=source_zip,
758 source_version=source_version,
759 target_zip=target_zip,
760 target_version=target_version,
761 output_zip=output_zip,
762 script=script,
763 metadata=metadata,
764 info_dict=OPTIONS.info_dict)
765
766 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
767 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
768 metadata["pre-build"] = source_fp
769 metadata["post-build"] = target_fp
770
771 source_boot = common.GetBootableImage(
772 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
773 OPTIONS.source_info_dict)
774 target_boot = common.GetBootableImage(
775 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
776 updating_boot = (not OPTIONS.two_step and
777 (source_boot.data != target_boot.data))
778
779 source_recovery = common.GetBootableImage(
780 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
781 OPTIONS.source_info_dict)
782 target_recovery = common.GetBootableImage(
783 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
784 updating_recovery = (source_recovery.data != target_recovery.data)
785
Doug Zongker3c84f562014-07-31 11:06:30 -0700786 system_diff = BlockDifference("system", output_zip)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700787 if HasVendorPartition(target_zip):
788 if not HasVendorPartition(source_zip):
789 raise RuntimeError("can't generate incremental that adds /vendor")
Doug Zongker3c84f562014-07-31 11:06:30 -0700790 vendor_diff = BlockDifference("vendor", output_zip)
Geremy Condra36bd3652014-02-06 19:45:10 -0800791
Michael Rungec6e3afd2014-05-05 11:55:47 -0700792 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
793 oem_dict = None
794 if oem_props is not None:
795 if OPTIONS.oem_source is None:
796 raise common.ExternalError("OEM source required for this build")
797 script.Mount("/oem")
798 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
799
800 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800801 device_specific.IncrementalOTA_Assertions()
802
803 # Two-step incremental package strategy (in chronological order,
804 # which is *not* the order in which the generated script has
805 # things):
806 #
807 # if stage is not "2/3" or "3/3":
808 # do verification on current system
809 # write recovery image to boot partition
810 # set stage to "2/3"
811 # reboot to boot partition and restart recovery
812 # else if stage is "2/3":
813 # write recovery image to recovery partition
814 # set stage to "3/3"
815 # reboot to recovery partition and restart recovery
816 # else:
817 # (stage must be "3/3")
818 # perform update:
819 # patch system files, etc.
820 # force full install of new boot image
821 # set up system to update recovery partition on first boot
822 # complete script normally (allow recovery to mark itself finished and reboot)
823
824 if OPTIONS.two_step:
825 if not OPTIONS.info_dict.get("multistage_support", None):
826 assert False, "two-step packages not supported by this build"
827 fs = OPTIONS.info_dict["fstab"]["/misc"]
828 assert fs.fs_type.upper() == "EMMC", \
829 "two-step packages only supported on devices with EMMC /misc partitions"
830 bcb_dev = {"bcb_dev": fs.device}
831 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
832 script.AppendExtra("""
833if get_stage("%(bcb_dev)s", "stage") == "2/3" then
834""" % bcb_dev)
835 script.AppendExtra("sleep(20);\n");
836 script.WriteRawImage("/recovery", "recovery.img")
837 script.AppendExtra("""
838set_stage("%(bcb_dev)s", "3/3");
839reboot_now("%(bcb_dev)s", "recovery");
840else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
841""" % bcb_dev)
842
843 script.Print("Verifying current system...")
844
845 device_specific.IncrementalOTA_VerifyBegin()
846
Michael Rungec6e3afd2014-05-05 11:55:47 -0700847 if oem_props is None:
848 script.AssertSomeFingerprint(source_fp, target_fp)
849 else:
850 script.AssertSomeThumbprint(
851 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
852 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800853
854 if updating_boot:
Geremy Condra36bd3652014-02-06 19:45:10 -0800855 d = common.Difference(target_boot, source_boot)
856 _, _, d = d.ComputePatch()
857 print "boot target: %d source: %d diff: %d" % (
858 target_boot.size, source_boot.size, len(d))
859
860 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
861
862 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
863
864 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
865 (boot_type, boot_device,
866 source_boot.size, source_boot.sha1,
867 target_boot.size, target_boot.sha1))
868
869 device_specific.IncrementalOTA_VerifyEnd()
870
871 if OPTIONS.two_step:
872 script.WriteRawImage("/boot", "recovery.img")
873 script.AppendExtra("""
874set_stage("%(bcb_dev)s", "2/3");
875reboot_now("%(bcb_dev)s", "");
876else
877""" % bcb_dev)
878
879 script.Comment("---- start making changes here ----")
880
881 device_specific.IncrementalOTA_InstallBegin()
882
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700883 if HasVendorPartition(target_zip):
884 script.Print("Patching vendor image...")
885 script.ShowProgress(0.1, 0)
886 script.Syspatch(vendor_diff.device,
887 vendor_diff.tgt_mapfilename, vendor_diff.tgt_sha1,
888 vendor_diff.src_mapfilename, vendor_diff.src_sha1,
889 vendor_diff.patch.name)
890 sys_progress = 0.8
891 else:
892 sys_progress = 0.9
893
Geremy Condra36bd3652014-02-06 19:45:10 -0800894 script.Print("Patching system image...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700895 script.ShowProgress(sys_progress, 0)
896 script.Syspatch(system_diff.device,
897 system_diff.tgt_mapfilename, system_diff.tgt_sha1,
898 system_diff.src_mapfilename, system_diff.src_sha1,
899 system_diff.patch.name)
Geremy Condra36bd3652014-02-06 19:45:10 -0800900
901 if OPTIONS.two_step:
902 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
903 script.WriteRawImage("/boot", "boot.img")
904 print "writing full boot image (forced by two-step mode)"
905
906 if not OPTIONS.two_step:
907 if updating_boot:
908 # Produce the boot image by applying a patch to the current
909 # contents of the boot partition, and write it back to the
910 # partition.
911 script.Print("Patching boot image...")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700912 script.ShowProgress(0.1, 10)
Geremy Condra36bd3652014-02-06 19:45:10 -0800913 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
914 % (boot_type, boot_device,
915 source_boot.size, source_boot.sha1,
916 target_boot.size, target_boot.sha1),
917 "-",
918 target_boot.size, target_boot.sha1,
919 source_boot.sha1, "patch/boot.img.p")
920 print "boot image changed; including."
921 else:
922 print "boot image unchanged; skipping."
923
924 # Do device-specific installation (eg, write radio image).
925 device_specific.IncrementalOTA_InstallEnd()
926
927 if OPTIONS.extra_script is not None:
928 script.AppendExtra(OPTIONS.extra_script)
929
Doug Zongker922206e2014-03-04 13:16:24 -0800930 if OPTIONS.wipe_user_data:
931 script.Print("Erasing user data...")
932 script.FormatPartition("/data")
933
Geremy Condra36bd3652014-02-06 19:45:10 -0800934 if OPTIONS.two_step:
935 script.AppendExtra("""
936set_stage("%(bcb_dev)s", "");
937endif;
938endif;
939""" % bcb_dev)
940
941 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800942 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800943 WriteMetadata(metadata, output_zip)
944
Doug Zongker32b527d2014-03-04 10:03:02 -0800945def ParseMap(map_str):
946 x = map_str.split()
947 assert int(x[0]) == 4096
948 assert int(x[1]) == len(x)-2
949 return int(x[0]), [int(i) for i in x[2:]]
950
951def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1):
952 src_blksize, src_regions = ParseMap(src_map)
953 tgt_blksize, tgt_regions = ParseMap(tgt_map)
954
955 with tempfile.NamedTemporaryFile() as src_file,\
956 tempfile.NamedTemporaryFile() as patch_file,\
Doug Zongker32b527d2014-03-04 10:03:02 -0800957 tempfile.NamedTemporaryFile() as src_map_file,\
958 tempfile.NamedTemporaryFile() as tgt_map_file:
959
960 src_total = sum(src_regions) * src_blksize
961 src_file.truncate(src_total)
962 p = 0
963 for i in range(0, len(src_regions), 2):
964 c, dc = src_regions[i:i+2]
965 src_file.write(src_muimg[p:(p+c*src_blksize)])
966 p += c*src_blksize
967 src_file.seek(dc*src_blksize, 1)
968 assert src_file.tell() == src_total
969
970 patch_file.write(patch_data)
971
Doug Zongker32b527d2014-03-04 10:03:02 -0800972 src_map_file.write(src_map)
973 tgt_map_file.write(tgt_map)
974
975 src_file.flush()
976 src_map_file.flush()
977 patch_file.flush()
Doug Zongker32b527d2014-03-04 10:03:02 -0800978 tgt_map_file.flush()
979
980 p = common.Run(["syspatch_host", src_file.name, src_map_file.name,
Doug Zongker1113e382014-06-13 10:38:32 -0700981 patch_file.name, src_file.name, tgt_map_file.name],
Doug Zongker32b527d2014-03-04 10:03:02 -0800982 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
983 stdoutdata, _ = p.communicate()
984 if p.returncode != 0:
985 print stdoutdata
986 raise ValueError("failed to reconstruct target system image from patch")
987
988 h = sha1()
Doug Zongker1113e382014-06-13 10:38:32 -0700989 src_file.seek(0, 0)
Doug Zongker32b527d2014-03-04 10:03:02 -0800990 for i in range(0, len(tgt_regions), 2):
991 c, dc = tgt_regions[i:i+2]
Doug Zongker1113e382014-06-13 10:38:32 -0700992 h.update(src_file.read(c*tgt_blksize))
993 src_file.seek(dc*tgt_blksize, 1)
Doug Zongker32b527d2014-03-04 10:03:02 -0800994
995 if h.hexdigest() != tgt_sha1:
996 raise ValueError("patch reconstructed incorrect target system image")
997
998 print "test of system image patch succeeded"
999
1000
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001001class FileDifference:
1002 def __init__(self, partition, source_zip, target_zip, output_zip):
1003 print "Loading target..."
1004 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
1005 print "Loading source..."
1006 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
1007
1008 self.verbatim_targets = verbatim_targets = []
1009 self.patch_list = patch_list = []
1010 diffs = []
1011 self.renames = renames = {}
1012 known_paths = set()
1013 largest_source_size = 0
1014
1015 matching_file_cache = {}
1016 for fn, sf in source_data.items():
1017 assert fn == sf.name
1018 matching_file_cache["path:" + fn] = sf
1019 if fn in target_data.keys():
1020 AddToKnownPaths(fn, known_paths)
1021 # Only allow eligibility for filename/sha matching
1022 # if there isn't a perfect path match.
1023 if target_data.get(sf.name) is None:
1024 matching_file_cache["file:" + fn.split("/")[-1]] = sf
1025 matching_file_cache["sha:" + sf.sha1] = sf
1026
1027 for fn in sorted(target_data.keys()):
1028 tf = target_data[fn]
1029 assert fn == tf.name
1030 sf = ClosestFileMatch(tf, matching_file_cache, renames)
1031 if sf is not None and sf.name != tf.name:
1032 print "File has moved from " + sf.name + " to " + tf.name
1033 renames[sf.name] = tf
1034
1035 if sf is None or fn in OPTIONS.require_verbatim:
1036 # This file should be included verbatim
1037 if fn in OPTIONS.prohibit_verbatim:
1038 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
1039 print "send", fn, "verbatim"
1040 tf.AddToZip(output_zip)
1041 verbatim_targets.append((fn, tf.size))
1042 if fn in target_data.keys():
1043 AddToKnownPaths(fn, known_paths)
1044 elif tf.sha1 != sf.sha1:
1045 # File is different; consider sending as a patch
1046 diffs.append(common.Difference(tf, sf))
1047 else:
1048 # Target file data identical to source (may still be renamed)
1049 pass
1050
1051 common.ComputeDifferences(diffs)
1052
1053 for diff in diffs:
1054 tf, sf, d = diff.GetPatch()
1055 path = "/".join(tf.name.split("/")[:-1])
1056 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1057 path not in known_paths:
1058 # patch is almost as big as the file; don't bother patching
1059 # or a patch + rename cannot take place due to the target
1060 # directory not existing
1061 tf.AddToZip(output_zip)
1062 verbatim_targets.append((tf.name, tf.size))
1063 if sf.name in renames:
1064 del renames[sf.name]
1065 AddToKnownPaths(tf.name, known_paths)
1066 else:
1067 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1068 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1069 largest_source_size = max(largest_source_size, sf.size)
1070
1071 self.largest_source_size = largest_source_size
1072
1073 def EmitVerification(self, script):
1074 so_far = 0
1075 for tf, sf, size, patch_sha in self.patch_list:
1076 if tf.name != sf.name:
1077 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1078 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1079 so_far += sf.size
1080 return so_far
1081
1082 def RemoveUnneededFiles(self, script, extras=()):
1083 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
1084 ["/"+i for i in sorted(self.source_data)
1085 if i not in self.target_data and
1086 i not in self.renames] +
1087 list(extras))
1088
1089 def TotalPatchSize(self):
1090 return sum(i[1].size for i in self.patch_list)
1091
1092 def EmitPatches(self, script, total_patch_size, so_far):
1093 self.deferred_patch_list = deferred_patch_list = []
1094 for item in self.patch_list:
1095 tf, sf, size, _ = item
1096 if tf.name == "system/build.prop":
1097 deferred_patch_list.append(item)
1098 continue
1099 if (sf.name != tf.name):
1100 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1101 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1102 so_far += tf.size
1103 script.SetProgress(so_far / total_patch_size)
1104 return so_far
1105
1106 def EmitDeferredPatches(self, script):
1107 for item in self.deferred_patch_list:
1108 tf, sf, size, _ = item
1109 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1110 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1111
1112 def EmitRenames(self, script):
1113 if len(self.renames) > 0:
1114 script.Print("Renaming files...")
1115 for src, tgt in self.renames.iteritems():
1116 print "Renaming " + src + " to " + tgt.name
1117 script.RenameFile(src, tgt.name)
1118
1119
1120
1121
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001122def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001123 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1124 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1125
Doug Zongker26e66192014-02-20 13:22:07 -08001126 if (OPTIONS.block_based and
1127 target_has_recovery_patch and
1128 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001129 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1130
Doug Zongker37974732010-09-16 17:44:38 -07001131 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1132 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001133
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001134 if source_version == 0:
1135 print ("WARNING: generating edify script for a source that "
1136 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001137 script = edify_generator.EdifyGenerator(source_version,
1138 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001139
Michael Runge6e836112014-04-15 17:40:21 -07001140 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1141 oem_dict = None
1142 if oem_props is not None:
1143 if OPTIONS.oem_source is None:
1144 raise common.ExternalError("OEM source required for this build")
1145 script.Mount("/oem")
1146 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1147
1148 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001149 OPTIONS.source_info_dict),
1150 "post-timestamp": GetBuildProp("ro.build.date.utc",
1151 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001152 }
1153
Doug Zongker05d3dea2009-06-22 11:32:31 -07001154 device_specific = common.DeviceSpecificParams(
1155 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001156 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001157 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001158 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001159 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001160 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001161 metadata=metadata,
1162 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001163
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001164 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge6e836112014-04-15 17:40:21 -07001165 script.Mount("/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001166 if HasVendorPartition(target_zip):
1167 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1168 script.Mount("/vendor")
1169 else:
1170 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001171
1172 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1173 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1174
1175 if oem_props is None:
1176 script.AssertSomeFingerprint(source_fp, target_fp)
1177 else:
1178 script.AssertSomeThumbprint(
1179 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1180 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1181
Doug Zongker2ea21062010-04-28 16:05:21 -07001182 metadata["pre-build"] = source_fp
1183 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001184
Doug Zongker55d93282011-01-25 17:03:34 -08001185 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001186 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1187 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001188 target_boot = common.GetBootableImage(
1189 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001190 updating_boot = (not OPTIONS.two_step and
1191 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001192
Doug Zongker55d93282011-01-25 17:03:34 -08001193 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001194 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1195 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001196 target_recovery = common.GetBootableImage(
1197 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001198 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001199
Doug Zongker881dd402009-09-20 14:03:55 -07001200 # Here's how we divide up the progress bar:
1201 # 0.1 for verifying the start state (PatchCheck calls)
1202 # 0.8 for applying patches (ApplyPatch calls)
1203 # 0.1 for unpacking verbatim files, symlinking, and doing the
1204 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001205
Michael Runge6e836112014-04-15 17:40:21 -07001206 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001207 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001208
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001209 # Two-step incremental package strategy (in chronological order,
1210 # which is *not* the order in which the generated script has
1211 # things):
1212 #
1213 # if stage is not "2/3" or "3/3":
1214 # do verification on current system
1215 # write recovery image to boot partition
1216 # set stage to "2/3"
1217 # reboot to boot partition and restart recovery
1218 # else if stage is "2/3":
1219 # write recovery image to recovery partition
1220 # set stage to "3/3"
1221 # reboot to recovery partition and restart recovery
1222 # else:
1223 # (stage must be "3/3")
1224 # perform update:
1225 # patch system files, etc.
1226 # force full install of new boot image
1227 # set up system to update recovery partition on first boot
1228 # complete script normally (allow recovery to mark itself finished and reboot)
1229
1230 if OPTIONS.two_step:
1231 if not OPTIONS.info_dict.get("multistage_support", None):
1232 assert False, "two-step packages not supported by this build"
1233 fs = OPTIONS.info_dict["fstab"]["/misc"]
1234 assert fs.fs_type.upper() == "EMMC", \
1235 "two-step packages only supported on devices with EMMC /misc partitions"
1236 bcb_dev = {"bcb_dev": fs.device}
1237 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1238 script.AppendExtra("""
1239if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1240""" % bcb_dev)
1241 script.AppendExtra("sleep(20);\n");
1242 script.WriteRawImage("/recovery", "recovery.img")
1243 script.AppendExtra("""
1244set_stage("%(bcb_dev)s", "3/3");
1245reboot_now("%(bcb_dev)s", "recovery");
1246else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1247""" % bcb_dev)
1248
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001249 script.Print("Verifying current system...")
1250
Doug Zongkere5ff5902012-01-17 10:55:37 -08001251 device_specific.IncrementalOTA_VerifyBegin()
1252
Doug Zongker881dd402009-09-20 14:03:55 -07001253 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001254 so_far = system_diff.EmitVerification(script)
1255 if vendor_diff:
1256 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001257
Doug Zongker5da317e2009-06-02 13:38:17 -07001258 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001259 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001260 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001261 print "boot target: %d source: %d diff: %d" % (
1262 target_boot.size, source_boot.size, len(d))
1263
Doug Zongker048e7ca2009-06-15 14:31:53 -07001264 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001265
Doug Zongker96a57e72010-09-26 14:57:41 -07001266 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001267
1268 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1269 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001270 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001271 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001272 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001273
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001274 size = []
1275 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1276 if vendor_diff:
1277 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1278 if size or updating_recovery or updating_boot:
1279 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001280
Doug Zongker05d3dea2009-06-22 11:32:31 -07001281 device_specific.IncrementalOTA_VerifyEnd()
1282
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001283 if OPTIONS.two_step:
1284 script.WriteRawImage("/boot", "recovery.img")
1285 script.AppendExtra("""
1286set_stage("%(bcb_dev)s", "2/3");
1287reboot_now("%(bcb_dev)s", "");
1288else
1289""" % bcb_dev)
1290
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001291 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001292
Doug Zongkere5ff5902012-01-17 10:55:37 -08001293 device_specific.IncrementalOTA_InstallBegin()
1294
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001295 if OPTIONS.two_step:
1296 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1297 script.WriteRawImage("/boot", "boot.img")
1298 print "writing full boot image (forced by two-step mode)"
1299
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001300 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001301 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1302 if vendor_diff:
1303 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001304
Doug Zongker881dd402009-09-20 14:03:55 -07001305 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001306 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1307 if vendor_diff:
1308 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001309 if updating_boot:
1310 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001311
1312 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001313 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1314 if vendor_diff:
1315 script.Print("Patching vendor files...")
1316 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001317
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001318 if not OPTIONS.two_step:
1319 if updating_boot:
1320 # Produce the boot image by applying a patch to the current
1321 # contents of the boot partition, and write it back to the
1322 # partition.
1323 script.Print("Patching boot image...")
1324 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1325 % (boot_type, boot_device,
1326 source_boot.size, source_boot.sha1,
1327 target_boot.size, target_boot.sha1),
1328 "-",
1329 target_boot.size, target_boot.sha1,
1330 source_boot.sha1, "patch/boot.img.p")
1331 so_far += target_boot.size
1332 script.SetProgress(so_far / total_patch_size)
1333 print "boot image changed; including."
1334 else:
1335 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001336
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001337 system_items = ItemSet("system", "META/filesystem_config.txt")
1338 if vendor_diff:
1339 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1340
Doug Zongkereef39442009-04-02 12:14:19 -07001341 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001342 # Recovery is generated as a patch using both the boot image
1343 # (which contains the same linux kernel as recovery) and the file
1344 # /system/etc/recovery-resource.dat (which contains all the images
1345 # used in the recovery UI) as sources. This lets us minimize the
1346 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001347 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001348 # For older builds where recovery-resource.dat is not present, we
1349 # use only the boot image as the source.
1350
Doug Zongkerc9253822014-02-04 12:17:58 -08001351 if not target_has_recovery_patch:
1352 def output_sink(fn, data):
1353 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001354 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001355
1356 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1357 target_recovery, target_boot)
1358 script.DeleteFiles(["/system/recovery-from-boot.p",
1359 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001360 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001361 else:
1362 print "recovery image unchanged; skipping."
1363
Doug Zongker881dd402009-09-20 14:03:55 -07001364 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001365
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001366 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1367 if vendor_diff:
1368 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1369
1370 temp_script = script.MakeTemporary()
1371 system_items.GetMetadata(target_zip)
1372 system_items.Get("system").SetPermissions(temp_script)
1373 if vendor_diff:
1374 vendor_items.GetMetadata(target_zip)
1375 vendor_items.Get("vendor").SetPermissions(temp_script)
1376
1377 # Note that this call will mess up the trees of Items, so make sure
1378 # we're done with them.
1379 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1380 if vendor_diff:
1381 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001382
1383 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001384 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1385
1386 # Delete all the symlinks in source that aren't in target. This
1387 # needs to happen before verbatim files are unpacked, in case a
1388 # symlink in the source is replaced by a real file in the target.
1389 to_delete = []
1390 for dest, link in source_symlinks:
1391 if link not in target_symlinks_d:
1392 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001393 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001394
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001395 if system_diff.verbatim_targets:
1396 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001397 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001398 if vendor_diff and vendor_diff.verbatim_targets:
1399 script.Print("Unpacking new vendor files...")
1400 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001401
Doug Zongkerc9253822014-02-04 12:17:58 -08001402 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001403 script.Print("Unpacking new recovery...")
1404 script.UnpackPackageDir("recovery", "/system")
1405
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001406 system_diff.EmitRenames(script)
1407 if vendor_diff:
1408 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001409
Doug Zongker05d3dea2009-06-22 11:32:31 -07001410 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001411
1412 # Create all the symlinks that don't already exist, or point to
1413 # somewhere different than what we want. Delete each symlink before
1414 # creating it, since the 'symlink' command won't overwrite.
1415 to_create = []
1416 for dest, link in target_symlinks:
1417 if link in source_symlinks_d:
1418 if dest != source_symlinks_d[link]:
1419 to_create.append((dest, link))
1420 else:
1421 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001422 script.DeleteFiles([i[1] for i in to_create])
1423 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001424
1425 # Now that the symlinks are created, we can set all the
1426 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001427 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001428
Doug Zongker881dd402009-09-20 14:03:55 -07001429 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001430 device_specific.IncrementalOTA_InstallEnd()
1431
Doug Zongker1c390a22009-05-14 19:06:36 -07001432 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001433 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001434
Doug Zongkere92f15a2011-08-26 13:46:40 -07001435 # Patch the build.prop file last, so if something fails but the
1436 # device can still come up, it appears to be the old build and will
1437 # get set the OTA package again to retry.
1438 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001439 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001440
Doug Zongker922206e2014-03-04 13:16:24 -08001441 if OPTIONS.wipe_user_data:
1442 script.Print("Erasing user data...")
1443 script.FormatPartition("/data")
1444
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001445 if OPTIONS.two_step:
1446 script.AppendExtra("""
1447set_stage("%(bcb_dev)s", "");
1448endif;
1449endif;
1450""" % bcb_dev)
1451
Doug Zongker25568482014-03-03 10:21:27 -08001452 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001453 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001454
1455
1456def main(argv):
1457
1458 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001459 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001460 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001461 elif o in ("-k", "--package_key"):
1462 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001463 elif o in ("-i", "--incremental_from"):
1464 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001465 elif o in ("-w", "--wipe_user_data"):
1466 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001467 elif o in ("-n", "--no_prereq"):
1468 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001469 elif o in ("-o", "--oem_settings"):
1470 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001471 elif o in ("-e", "--extra_script"):
1472 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001473 elif o in ("-a", "--aslr_mode"):
1474 if a in ("on", "On", "true", "True", "yes", "Yes"):
1475 OPTIONS.aslr_mode = True
1476 else:
1477 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001478 elif o in ("-t", "--worker_threads"):
1479 if a.isdigit():
1480 OPTIONS.worker_threads = int(a)
1481 else:
1482 raise ValueError("Cannot parse value %r for option %r - only "
1483 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001484 elif o in ("-2", "--two_step"):
1485 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001486 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001487 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001488 elif o == "--block":
1489 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001490 elif o in ("-b", "--binary"):
1491 OPTIONS.updater_binary = a
Doug Zongkereef39442009-04-02 12:14:19 -07001492 else:
1493 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001494 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001495
1496 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001497 extra_opts="b:k:i:d:wne:t:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001498 extra_long_opts=["board_config=",
1499 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001500 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001501 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001502 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001503 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001504 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001505 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001506 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001507 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001508 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001509 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001510 "oem_settings=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001511 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001512 extra_option_handler=option_handler)
1513
1514 if len(args) != 2:
1515 common.Usage(__doc__)
1516 sys.exit(1)
1517
Doug Zongker1c390a22009-05-14 19:06:36 -07001518 if OPTIONS.extra_script is not None:
1519 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1520
Doug Zongkereef39442009-04-02 12:14:19 -07001521 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001522 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001523
Doug Zongkereef39442009-04-02 12:14:19 -07001524 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001525 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001526
1527 # If this image was originally labelled with SELinux contexts, make sure we
1528 # also apply the labels in our new image. During building, the "file_contexts"
1529 # is in the out/ directory tree, but for repacking from target-files.zip it's
1530 # in the root directory of the ramdisk.
1531 if "selinux_fc" in OPTIONS.info_dict:
1532 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1533 "file_contexts")
1534
Doug Zongker37974732010-09-16 17:44:38 -07001535 if OPTIONS.verbose:
1536 print "--- target info ---"
1537 common.DumpInfoDict(OPTIONS.info_dict)
1538
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001539 # If the caller explicitly specified the device-specific extensions
1540 # path via -s/--device_specific, use that. Otherwise, use
1541 # META/releasetools.py if it is present in the target target_files.
1542 # Otherwise, take the path of the file from 'tool_extensions' in the
1543 # info dict and look for that in the local filesystem, relative to
1544 # the current directory.
1545
Doug Zongker37974732010-09-16 17:44:38 -07001546 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001547 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1548 if os.path.exists(from_input):
1549 print "(using device-specific extensions from target_files)"
1550 OPTIONS.device_specific = from_input
1551 else:
1552 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1553
Doug Zongker37974732010-09-16 17:44:38 -07001554 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001555 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001556
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001557 if OPTIONS.no_signing:
1558 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1559 else:
1560 temp_zip_file = tempfile.NamedTemporaryFile()
1561 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1562 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001563
1564 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001565 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001566 if OPTIONS.package_key is None:
1567 OPTIONS.package_key = OPTIONS.info_dict.get(
1568 "default_system_dev_certificate",
1569 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001570 else:
1571 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001572 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001573 OPTIONS.target_info_dict = OPTIONS.info_dict
1574 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongker5fad2032014-02-24 08:13:45 -08001575 if "selinux_fc" in OPTIONS.source_info_dict:
1576 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1577 "file_contexts")
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001578 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001579 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001580 "default_system_dev_certificate",
1581 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001582 if OPTIONS.verbose:
1583 print "--- source info ---"
1584 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001585 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001586
1587 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001588
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001589 if not OPTIONS.no_signing:
1590 SignOutput(temp_zip_file.name, args[1])
1591 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001592
1593 common.Cleanup()
1594
1595 print "done."
1596
1597
1598if __name__ == '__main__':
1599 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001600 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001601 main(sys.argv[1:])
1602 except common.ExternalError, e:
1603 print
1604 print " ERROR: %s" % (e,)
1605 print
1606 sys.exit(1)