blob: 5f2354c298296306289909a1ccfcee0a2904e45c [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 Zongker01ce19c2014-02-04 13:48:15 -0800101import img_from_target_files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700102import edify_generator
Geremy Condra36bd3652014-02-06 19:45:10 -0800103import build_image
Doug Zongkereef39442009-04-02 12:14:19 -0700104
105OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700106OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700107OPTIONS.incremental_source = None
108OPTIONS.require_verbatim = set()
109OPTIONS.prohibit_verbatim = set(("system/build.prop",))
110OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700111OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700112OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700113OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700114OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -0700115OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800116OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900117OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800118OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800119OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700120OPTIONS.oem_source = None
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 Zongkerc77a9ad2010-09-16 11:28:43 -0700421def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700422 # TODO: how to determine this? We don't know what version it will
423 # be installed on top of. For now, we expect the API just won't
424 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700425 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700426
Michael Runge6e836112014-04-15 17:40:21 -0700427 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
428 oem_dict = None
429 if oem_props is not None:
430 if OPTIONS.oem_source is None:
431 raise common.ExternalError("OEM source required for this build")
432 script.Mount("/oem")
433 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
434
435 metadata = {"post-build": CalculateFingerprint(
436 oem_props, oem_dict, OPTIONS.info_dict),
437 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700438 OPTIONS.info_dict),
439 "post-timestamp": GetBuildProp("ro.build.date.utc",
440 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700441 }
442
Doug Zongker05d3dea2009-06-22 11:32:31 -0700443 device_specific = common.DeviceSpecificParams(
444 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700445 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700446 output_zip=output_zip,
447 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700448 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700449 metadata=metadata,
450 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700451
Doug Zongkerc9253822014-02-04 12:17:58 -0800452 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800453 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800454
Doug Zongker962069c2009-04-23 11:41:58 -0700455 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700456 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700457 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
458 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700459
Michael Runge6e836112014-04-15 17:40:21 -0700460 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700461 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800462
463 # Two-step package strategy (in chronological order, which is *not*
464 # the order in which the generated script has things):
465 #
466 # if stage is not "2/3" or "3/3":
467 # write recovery image to boot partition
468 # set stage to "2/3"
469 # reboot to boot partition and restart recovery
470 # else if stage is "2/3":
471 # write recovery image to recovery partition
472 # set stage to "3/3"
473 # reboot to recovery partition and restart recovery
474 # else:
475 # (stage must be "3/3")
476 # set stage to ""
477 # do normal full package installation:
478 # wipe and install system, boot image, etc.
479 # set up system to update recovery partition on first boot
480 # complete script normally (allow recovery to mark itself finished and reboot)
481
482 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
483 OPTIONS.input_tmp, "RECOVERY")
484 if OPTIONS.two_step:
485 if not OPTIONS.info_dict.get("multistage_support", None):
486 assert False, "two-step packages not supported by this build"
487 fs = OPTIONS.info_dict["fstab"]["/misc"]
488 assert fs.fs_type.upper() == "EMMC", \
489 "two-step packages only supported on devices with EMMC /misc partitions"
490 bcb_dev = {"bcb_dev": fs.device}
491 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
492 script.AppendExtra("""
493if get_stage("%(bcb_dev)s", "stage") == "2/3" then
494""" % bcb_dev)
495 script.WriteRawImage("/recovery", "recovery.img")
496 script.AppendExtra("""
497set_stage("%(bcb_dev)s", "3/3");
498reboot_now("%(bcb_dev)s", "recovery");
499else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
500""" % bcb_dev)
501
Doug Zongkere5ff5902012-01-17 10:55:37 -0800502 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700503
Doug Zongker01ce19c2014-02-04 13:48:15 -0800504 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700505
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700506 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800507 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700508 if HasVendorPartition(input_zip):
509 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700510
Kenny Rootf32dc712012-04-08 10:42:34 -0700511 if "selinux_fc" in OPTIONS.info_dict:
512 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500513
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700514 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700515 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800516 if block_based:
Doug Zongker5fad2032014-02-24 08:13:45 -0800517 mapdata, data = img_from_target_files.BuildSystem(
518 OPTIONS.input_tmp, OPTIONS.info_dict,
519 sparse=False, map_file=True)
520
521 common.ZipWriteStr(output_zip, "system.map", mapdata)
522 common.ZipWriteStr(output_zip, "system.muimg", data)
523 script.WipeBlockDevice("/system")
524 script.WriteRawImage("/system", "system.muimg", mapfn="system.map")
Doug Zongker01ce19c2014-02-04 13:48:15 -0800525 else:
526 script.FormatPartition("/system")
527 script.Mount("/system")
528 if not has_recovery_patch:
529 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800530 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700531
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700532 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800533 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700534
Doug Zongker55d93282011-01-25 17:03:34 -0800535 boot_img = common.GetBootableImage("boot.img", "boot.img",
536 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800537
Doug Zongker91a99c22014-05-09 13:15:01 -0700538 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800539 def output_sink(fn, data):
540 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700541 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800542
543 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
544 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700545
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700546 system_items.GetMetadata(input_zip)
547 system_items.Get("system").SetPermissions(script)
548
549 if HasVendorPartition(input_zip):
550 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
551 script.ShowProgress(0.1, 0)
552
553 if block_based:
554 mapdata, data = img_from_target_files.BuildVendor(
555 OPTIONS.input_tmp, OPTIONS.info_dict,
556 sparse=False, map_file=True)
557
558 common.ZipWriteStr(output_zip, "vendor.map", mapdata)
559 common.ZipWriteStr(output_zip, "vendor.muimg", data)
560 script.WipeBlockDevice("/vendor")
561 script.WriteRawImage("/vendor", "vendor.muimg", mapfn="vendor.map")
562 else:
563 script.FormatPartition("/vendor")
564 script.Mount("/vendor")
565 script.UnpackPackageDir("vendor", "/vendor")
566
567 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
568 script.MakeSymlinks(symlinks)
569
570 vendor_items.GetMetadata(input_zip)
571 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700572
Doug Zongker37974732010-09-16 17:44:38 -0700573 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700574 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700575
Doug Zongker01ce19c2014-02-04 13:48:15 -0800576 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700577 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700578
Doug Zongker01ce19c2014-02-04 13:48:15 -0800579 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700580 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700581
Doug Zongker1c390a22009-05-14 19:06:36 -0700582 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700583 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700584
Doug Zongker14833602010-02-02 13:12:04 -0800585 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800586
Doug Zongker922206e2014-03-04 13:16:24 -0800587 if OPTIONS.wipe_user_data:
588 script.ShowProgress(0.1, 10)
589 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700590
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800591 if OPTIONS.two_step:
592 script.AppendExtra("""
593set_stage("%(bcb_dev)s", "");
594""" % bcb_dev)
595 script.AppendExtra("else\n")
596 script.WriteRawImage("/boot", "recovery.img")
597 script.AppendExtra("""
598set_stage("%(bcb_dev)s", "2/3");
599reboot_now("%(bcb_dev)s", "");
600endif;
601endif;
602""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800603 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700604 WriteMetadata(metadata, output_zip)
605
Stephen Smalley56882bf2012-02-09 13:36:21 -0500606def WritePolicyConfig(file_context, output_zip):
607 f = open(file_context, 'r');
608 basename = os.path.basename(file_context)
609 common.ZipWriteStr(output_zip, basename, f.read())
610
Doug Zongker2ea21062010-04-28 16:05:21 -0700611
612def WriteMetadata(metadata, output_zip):
613 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
614 "".join(["%s=%s\n" % kv
615 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700616
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700617def LoadPartitionFiles(z, partition):
618 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700619 ZipFile, and return a dict of {filename: File object}."""
620 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700621 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700622 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700623 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700624 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700625 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700626 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700627 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800628 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700629
630
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700631def GetBuildProp(prop, info_dict):
632 """Return the fingerprint of the build of a given target-files info_dict."""
633 try:
634 return info_dict.get("build.prop", {})[prop]
635 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700636 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700637
Michael Runge4038aa82013-12-13 18:06:28 -0800638def AddToKnownPaths(filename, known_paths):
639 if filename[-1] == "/":
640 return
641 dirs = filename.split("/")[:-1]
642 while len(dirs) > 0:
643 path = "/".join(dirs)
644 if path in known_paths:
645 break;
646 known_paths.add(path)
647 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700648
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700649class BlockDifference:
650 def __init__(self, partition, builder, output_zip):
651 with tempfile.NamedTemporaryFile() as src_file:
652 with tempfile.NamedTemporaryFile() as tgt_file:
653 print "building source " + partition + " image..."
654 src_file = tempfile.NamedTemporaryFile()
655 src_mapdata, src_data = builder(OPTIONS.source_tmp,
656 OPTIONS.source_info_dict,
657 sparse=False, map_file=True)
658
659 self.src_sha1 = sha1(src_data).hexdigest()
660 print "source " + partition + " sha1:", self.src_sha1
661 src_file.write(src_data)
662
663 print "building target " + partition + " image..."
664 tgt_file = tempfile.NamedTemporaryFile()
665 tgt_mapdata, tgt_data = builder(OPTIONS.target_tmp,
666 OPTIONS.target_info_dict,
667 sparse=False, map_file=True)
668 self.tgt_sha1 = sha1(tgt_data).hexdigest()
669 print "target " + partition + " sha1:", self.tgt_sha1
670 tgt_len = len(tgt_data)
671 tgt_file.write(tgt_data)
672
673 system_type, self.device = common.GetTypeAndDevice("/" + partition,
674 OPTIONS.info_dict)
675 self.patch = common.MakePartitionPatch(src_file, tgt_file, partition)
676
677 TestBlockPatch(src_data, src_mapdata, self.patch.data,
678 tgt_mapdata, self.tgt_sha1)
679 src_data = None
680 tgt_data = None
681
682 self.patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
683 self.src_mapfilename = self.patch.name + ".src.map"
684 common.ZipWriteStr(output_zip, self.src_mapfilename, src_mapdata)
685 self.tgt_mapfilename = self.patch.name + ".tgt.map"
686 common.ZipWriteStr(output_zip, self.tgt_mapfilename, tgt_mapdata)
687
Geremy Condra36bd3652014-02-06 19:45:10 -0800688def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
689 source_version = OPTIONS.source_info_dict["recovery_api_version"]
690 target_version = OPTIONS.target_info_dict["recovery_api_version"]
691
692 if source_version == 0:
693 print ("WARNING: generating edify script for a source that "
694 "can't install it.")
695 script = edify_generator.EdifyGenerator(source_version,
696 OPTIONS.target_info_dict)
697
698 metadata = {"pre-device": GetBuildProp("ro.product.device",
699 OPTIONS.source_info_dict),
700 "post-timestamp": GetBuildProp("ro.build.date.utc",
701 OPTIONS.target_info_dict),
702 }
703
704 device_specific = common.DeviceSpecificParams(
705 source_zip=source_zip,
706 source_version=source_version,
707 target_zip=target_zip,
708 target_version=target_version,
709 output_zip=output_zip,
710 script=script,
711 metadata=metadata,
712 info_dict=OPTIONS.info_dict)
713
714 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
715 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
716 metadata["pre-build"] = source_fp
717 metadata["post-build"] = target_fp
718
719 source_boot = common.GetBootableImage(
720 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
721 OPTIONS.source_info_dict)
722 target_boot = common.GetBootableImage(
723 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
724 updating_boot = (not OPTIONS.two_step and
725 (source_boot.data != target_boot.data))
726
727 source_recovery = common.GetBootableImage(
728 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
729 OPTIONS.source_info_dict)
730 target_recovery = common.GetBootableImage(
731 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
732 updating_recovery = (source_recovery.data != target_recovery.data)
733
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700734 system_diff = BlockDifference("system", img_from_target_files.BuildSystem,
735 output_zip)
736 if HasVendorPartition(target_zip):
737 if not HasVendorPartition(source_zip):
738 raise RuntimeError("can't generate incremental that adds /vendor")
739 vendor_diff = BlockDifference("vendor", img_from_target_files.BuildVendor,
740 output_zip)
Geremy Condra36bd3652014-02-06 19:45:10 -0800741
Michael Rungec6e3afd2014-05-05 11:55:47 -0700742 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
743 oem_dict = None
744 if oem_props is not None:
745 if OPTIONS.oem_source is None:
746 raise common.ExternalError("OEM source required for this build")
747 script.Mount("/oem")
748 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
749
750 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800751 device_specific.IncrementalOTA_Assertions()
752
753 # Two-step incremental package strategy (in chronological order,
754 # which is *not* the order in which the generated script has
755 # things):
756 #
757 # if stage is not "2/3" or "3/3":
758 # do verification on current system
759 # write recovery image to boot partition
760 # set stage to "2/3"
761 # reboot to boot partition and restart recovery
762 # else if stage is "2/3":
763 # write recovery image to recovery partition
764 # set stage to "3/3"
765 # reboot to recovery partition and restart recovery
766 # else:
767 # (stage must be "3/3")
768 # perform update:
769 # patch system files, etc.
770 # force full install of new boot image
771 # set up system to update recovery partition on first boot
772 # complete script normally (allow recovery to mark itself finished and reboot)
773
774 if OPTIONS.two_step:
775 if not OPTIONS.info_dict.get("multistage_support", None):
776 assert False, "two-step packages not supported by this build"
777 fs = OPTIONS.info_dict["fstab"]["/misc"]
778 assert fs.fs_type.upper() == "EMMC", \
779 "two-step packages only supported on devices with EMMC /misc partitions"
780 bcb_dev = {"bcb_dev": fs.device}
781 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
782 script.AppendExtra("""
783if get_stage("%(bcb_dev)s", "stage") == "2/3" then
784""" % bcb_dev)
785 script.AppendExtra("sleep(20);\n");
786 script.WriteRawImage("/recovery", "recovery.img")
787 script.AppendExtra("""
788set_stage("%(bcb_dev)s", "3/3");
789reboot_now("%(bcb_dev)s", "recovery");
790else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
791""" % bcb_dev)
792
793 script.Print("Verifying current system...")
794
795 device_specific.IncrementalOTA_VerifyBegin()
796
Michael Rungec6e3afd2014-05-05 11:55:47 -0700797 if oem_props is None:
798 script.AssertSomeFingerprint(source_fp, target_fp)
799 else:
800 script.AssertSomeThumbprint(
801 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
802 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800803
804 if updating_boot:
Geremy Condra36bd3652014-02-06 19:45:10 -0800805 d = common.Difference(target_boot, source_boot)
806 _, _, d = d.ComputePatch()
807 print "boot target: %d source: %d diff: %d" % (
808 target_boot.size, source_boot.size, len(d))
809
810 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
811
812 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
813
814 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
815 (boot_type, boot_device,
816 source_boot.size, source_boot.sha1,
817 target_boot.size, target_boot.sha1))
818
819 device_specific.IncrementalOTA_VerifyEnd()
820
821 if OPTIONS.two_step:
822 script.WriteRawImage("/boot", "recovery.img")
823 script.AppendExtra("""
824set_stage("%(bcb_dev)s", "2/3");
825reboot_now("%(bcb_dev)s", "");
826else
827""" % bcb_dev)
828
829 script.Comment("---- start making changes here ----")
830
831 device_specific.IncrementalOTA_InstallBegin()
832
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700833 if HasVendorPartition(target_zip):
834 script.Print("Patching vendor image...")
835 script.ShowProgress(0.1, 0)
836 script.Syspatch(vendor_diff.device,
837 vendor_diff.tgt_mapfilename, vendor_diff.tgt_sha1,
838 vendor_diff.src_mapfilename, vendor_diff.src_sha1,
839 vendor_diff.patch.name)
840 sys_progress = 0.8
841 else:
842 sys_progress = 0.9
843
Geremy Condra36bd3652014-02-06 19:45:10 -0800844 script.Print("Patching system image...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700845 script.ShowProgress(sys_progress, 0)
846 script.Syspatch(system_diff.device,
847 system_diff.tgt_mapfilename, system_diff.tgt_sha1,
848 system_diff.src_mapfilename, system_diff.src_sha1,
849 system_diff.patch.name)
Geremy Condra36bd3652014-02-06 19:45:10 -0800850
851 if OPTIONS.two_step:
852 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
853 script.WriteRawImage("/boot", "boot.img")
854 print "writing full boot image (forced by two-step mode)"
855
856 if not OPTIONS.two_step:
857 if updating_boot:
858 # Produce the boot image by applying a patch to the current
859 # contents of the boot partition, and write it back to the
860 # partition.
861 script.Print("Patching boot image...")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700862 script.ShowProgress(0.1, 10)
Geremy Condra36bd3652014-02-06 19:45:10 -0800863 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
864 % (boot_type, boot_device,
865 source_boot.size, source_boot.sha1,
866 target_boot.size, target_boot.sha1),
867 "-",
868 target_boot.size, target_boot.sha1,
869 source_boot.sha1, "patch/boot.img.p")
870 print "boot image changed; including."
871 else:
872 print "boot image unchanged; skipping."
873
874 # Do device-specific installation (eg, write radio image).
875 device_specific.IncrementalOTA_InstallEnd()
876
877 if OPTIONS.extra_script is not None:
878 script.AppendExtra(OPTIONS.extra_script)
879
Doug Zongker922206e2014-03-04 13:16:24 -0800880 if OPTIONS.wipe_user_data:
881 script.Print("Erasing user data...")
882 script.FormatPartition("/data")
883
Geremy Condra36bd3652014-02-06 19:45:10 -0800884 if OPTIONS.two_step:
885 script.AppendExtra("""
886set_stage("%(bcb_dev)s", "");
887endif;
888endif;
889""" % bcb_dev)
890
891 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800892 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800893 WriteMetadata(metadata, output_zip)
894
Doug Zongker32b527d2014-03-04 10:03:02 -0800895def ParseMap(map_str):
896 x = map_str.split()
897 assert int(x[0]) == 4096
898 assert int(x[1]) == len(x)-2
899 return int(x[0]), [int(i) for i in x[2:]]
900
901def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1):
902 src_blksize, src_regions = ParseMap(src_map)
903 tgt_blksize, tgt_regions = ParseMap(tgt_map)
904
905 with tempfile.NamedTemporaryFile() as src_file,\
906 tempfile.NamedTemporaryFile() as patch_file,\
Doug Zongker32b527d2014-03-04 10:03:02 -0800907 tempfile.NamedTemporaryFile() as src_map_file,\
908 tempfile.NamedTemporaryFile() as tgt_map_file:
909
910 src_total = sum(src_regions) * src_blksize
911 src_file.truncate(src_total)
912 p = 0
913 for i in range(0, len(src_regions), 2):
914 c, dc = src_regions[i:i+2]
915 src_file.write(src_muimg[p:(p+c*src_blksize)])
916 p += c*src_blksize
917 src_file.seek(dc*src_blksize, 1)
918 assert src_file.tell() == src_total
919
920 patch_file.write(patch_data)
921
Doug Zongker32b527d2014-03-04 10:03:02 -0800922 src_map_file.write(src_map)
923 tgt_map_file.write(tgt_map)
924
925 src_file.flush()
926 src_map_file.flush()
927 patch_file.flush()
Doug Zongker32b527d2014-03-04 10:03:02 -0800928 tgt_map_file.flush()
929
930 p = common.Run(["syspatch_host", src_file.name, src_map_file.name,
Doug Zongker1113e382014-06-13 10:38:32 -0700931 patch_file.name, src_file.name, tgt_map_file.name],
Doug Zongker32b527d2014-03-04 10:03:02 -0800932 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
933 stdoutdata, _ = p.communicate()
934 if p.returncode != 0:
935 print stdoutdata
936 raise ValueError("failed to reconstruct target system image from patch")
937
938 h = sha1()
Doug Zongker1113e382014-06-13 10:38:32 -0700939 src_file.seek(0, 0)
Doug Zongker32b527d2014-03-04 10:03:02 -0800940 for i in range(0, len(tgt_regions), 2):
941 c, dc = tgt_regions[i:i+2]
Doug Zongker1113e382014-06-13 10:38:32 -0700942 h.update(src_file.read(c*tgt_blksize))
943 src_file.seek(dc*tgt_blksize, 1)
Doug Zongker32b527d2014-03-04 10:03:02 -0800944
945 if h.hexdigest() != tgt_sha1:
946 raise ValueError("patch reconstructed incorrect target system image")
947
948 print "test of system image patch succeeded"
949
950
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700951class FileDifference:
952 def __init__(self, partition, source_zip, target_zip, output_zip):
953 print "Loading target..."
954 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
955 print "Loading source..."
956 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
957
958 self.verbatim_targets = verbatim_targets = []
959 self.patch_list = patch_list = []
960 diffs = []
961 self.renames = renames = {}
962 known_paths = set()
963 largest_source_size = 0
964
965 matching_file_cache = {}
966 for fn, sf in source_data.items():
967 assert fn == sf.name
968 matching_file_cache["path:" + fn] = sf
969 if fn in target_data.keys():
970 AddToKnownPaths(fn, known_paths)
971 # Only allow eligibility for filename/sha matching
972 # if there isn't a perfect path match.
973 if target_data.get(sf.name) is None:
974 matching_file_cache["file:" + fn.split("/")[-1]] = sf
975 matching_file_cache["sha:" + sf.sha1] = sf
976
977 for fn in sorted(target_data.keys()):
978 tf = target_data[fn]
979 assert fn == tf.name
980 sf = ClosestFileMatch(tf, matching_file_cache, renames)
981 if sf is not None and sf.name != tf.name:
982 print "File has moved from " + sf.name + " to " + tf.name
983 renames[sf.name] = tf
984
985 if sf is None or fn in OPTIONS.require_verbatim:
986 # This file should be included verbatim
987 if fn in OPTIONS.prohibit_verbatim:
988 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
989 print "send", fn, "verbatim"
990 tf.AddToZip(output_zip)
991 verbatim_targets.append((fn, tf.size))
992 if fn in target_data.keys():
993 AddToKnownPaths(fn, known_paths)
994 elif tf.sha1 != sf.sha1:
995 # File is different; consider sending as a patch
996 diffs.append(common.Difference(tf, sf))
997 else:
998 # Target file data identical to source (may still be renamed)
999 pass
1000
1001 common.ComputeDifferences(diffs)
1002
1003 for diff in diffs:
1004 tf, sf, d = diff.GetPatch()
1005 path = "/".join(tf.name.split("/")[:-1])
1006 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1007 path not in known_paths:
1008 # patch is almost as big as the file; don't bother patching
1009 # or a patch + rename cannot take place due to the target
1010 # directory not existing
1011 tf.AddToZip(output_zip)
1012 verbatim_targets.append((tf.name, tf.size))
1013 if sf.name in renames:
1014 del renames[sf.name]
1015 AddToKnownPaths(tf.name, known_paths)
1016 else:
1017 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1018 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1019 largest_source_size = max(largest_source_size, sf.size)
1020
1021 self.largest_source_size = largest_source_size
1022
1023 def EmitVerification(self, script):
1024 so_far = 0
1025 for tf, sf, size, patch_sha in self.patch_list:
1026 if tf.name != sf.name:
1027 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1028 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1029 so_far += sf.size
1030 return so_far
1031
1032 def RemoveUnneededFiles(self, script, extras=()):
1033 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
1034 ["/"+i for i in sorted(self.source_data)
1035 if i not in self.target_data and
1036 i not in self.renames] +
1037 list(extras))
1038
1039 def TotalPatchSize(self):
1040 return sum(i[1].size for i in self.patch_list)
1041
1042 def EmitPatches(self, script, total_patch_size, so_far):
1043 self.deferred_patch_list = deferred_patch_list = []
1044 for item in self.patch_list:
1045 tf, sf, size, _ = item
1046 if tf.name == "system/build.prop":
1047 deferred_patch_list.append(item)
1048 continue
1049 if (sf.name != tf.name):
1050 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1051 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1052 so_far += tf.size
1053 script.SetProgress(so_far / total_patch_size)
1054 return so_far
1055
1056 def EmitDeferredPatches(self, script):
1057 for item in self.deferred_patch_list:
1058 tf, sf, size, _ = item
1059 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1060 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1061
1062 def EmitRenames(self, script):
1063 if len(self.renames) > 0:
1064 script.Print("Renaming files...")
1065 for src, tgt in self.renames.iteritems():
1066 print "Renaming " + src + " to " + tgt.name
1067 script.RenameFile(src, tgt.name)
1068
1069
1070
1071
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001072def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001073 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1074 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1075
Doug Zongker26e66192014-02-20 13:22:07 -08001076 if (OPTIONS.block_based and
1077 target_has_recovery_patch and
1078 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001079 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1080
Doug Zongker37974732010-09-16 17:44:38 -07001081 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1082 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001083
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001084 if source_version == 0:
1085 print ("WARNING: generating edify script for a source that "
1086 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001087 script = edify_generator.EdifyGenerator(source_version,
1088 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001089
Michael Runge6e836112014-04-15 17:40:21 -07001090 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1091 oem_dict = None
1092 if oem_props is not None:
1093 if OPTIONS.oem_source is None:
1094 raise common.ExternalError("OEM source required for this build")
1095 script.Mount("/oem")
1096 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1097
1098 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001099 OPTIONS.source_info_dict),
1100 "post-timestamp": GetBuildProp("ro.build.date.utc",
1101 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001102 }
1103
Doug Zongker05d3dea2009-06-22 11:32:31 -07001104 device_specific = common.DeviceSpecificParams(
1105 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001106 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001107 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001108 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001109 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001110 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001111 metadata=metadata,
1112 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001113
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001114 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge6e836112014-04-15 17:40:21 -07001115 script.Mount("/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001116 if HasVendorPartition(target_zip):
1117 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1118 script.Mount("/vendor")
1119 else:
1120 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001121
1122 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1123 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1124
1125 if oem_props is None:
1126 script.AssertSomeFingerprint(source_fp, target_fp)
1127 else:
1128 script.AssertSomeThumbprint(
1129 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1130 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1131
Doug Zongker2ea21062010-04-28 16:05:21 -07001132 metadata["pre-build"] = source_fp
1133 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001134
Doug Zongker55d93282011-01-25 17:03:34 -08001135 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001136 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1137 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001138 target_boot = common.GetBootableImage(
1139 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001140 updating_boot = (not OPTIONS.two_step and
1141 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001142
Doug Zongker55d93282011-01-25 17:03:34 -08001143 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001144 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1145 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001146 target_recovery = common.GetBootableImage(
1147 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001148 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001149
Doug Zongker881dd402009-09-20 14:03:55 -07001150 # Here's how we divide up the progress bar:
1151 # 0.1 for verifying the start state (PatchCheck calls)
1152 # 0.8 for applying patches (ApplyPatch calls)
1153 # 0.1 for unpacking verbatim files, symlinking, and doing the
1154 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001155
Michael Runge6e836112014-04-15 17:40:21 -07001156 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001157 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001158
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001159 # Two-step incremental package strategy (in chronological order,
1160 # which is *not* the order in which the generated script has
1161 # things):
1162 #
1163 # if stage is not "2/3" or "3/3":
1164 # do verification on current system
1165 # write recovery image to boot partition
1166 # set stage to "2/3"
1167 # reboot to boot partition and restart recovery
1168 # else if stage is "2/3":
1169 # write recovery image to recovery partition
1170 # set stage to "3/3"
1171 # reboot to recovery partition and restart recovery
1172 # else:
1173 # (stage must be "3/3")
1174 # perform update:
1175 # patch system files, etc.
1176 # force full install of new boot image
1177 # set up system to update recovery partition on first boot
1178 # complete script normally (allow recovery to mark itself finished and reboot)
1179
1180 if OPTIONS.two_step:
1181 if not OPTIONS.info_dict.get("multistage_support", None):
1182 assert False, "two-step packages not supported by this build"
1183 fs = OPTIONS.info_dict["fstab"]["/misc"]
1184 assert fs.fs_type.upper() == "EMMC", \
1185 "two-step packages only supported on devices with EMMC /misc partitions"
1186 bcb_dev = {"bcb_dev": fs.device}
1187 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1188 script.AppendExtra("""
1189if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1190""" % bcb_dev)
1191 script.AppendExtra("sleep(20);\n");
1192 script.WriteRawImage("/recovery", "recovery.img")
1193 script.AppendExtra("""
1194set_stage("%(bcb_dev)s", "3/3");
1195reboot_now("%(bcb_dev)s", "recovery");
1196else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1197""" % bcb_dev)
1198
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001199 script.Print("Verifying current system...")
1200
Doug Zongkere5ff5902012-01-17 10:55:37 -08001201 device_specific.IncrementalOTA_VerifyBegin()
1202
Doug Zongker881dd402009-09-20 14:03:55 -07001203 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001204 so_far = system_diff.EmitVerification(script)
1205 if vendor_diff:
1206 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001207
Doug Zongker5da317e2009-06-02 13:38:17 -07001208 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001209 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001210 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001211 print "boot target: %d source: %d diff: %d" % (
1212 target_boot.size, source_boot.size, len(d))
1213
Doug Zongker048e7ca2009-06-15 14:31:53 -07001214 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001215
Doug Zongker96a57e72010-09-26 14:57:41 -07001216 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001217
1218 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1219 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001220 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001221 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001222 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001223
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001224 size = []
1225 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1226 if vendor_diff:
1227 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1228 if size or updating_recovery or updating_boot:
1229 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001230
Doug Zongker05d3dea2009-06-22 11:32:31 -07001231 device_specific.IncrementalOTA_VerifyEnd()
1232
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001233 if OPTIONS.two_step:
1234 script.WriteRawImage("/boot", "recovery.img")
1235 script.AppendExtra("""
1236set_stage("%(bcb_dev)s", "2/3");
1237reboot_now("%(bcb_dev)s", "");
1238else
1239""" % bcb_dev)
1240
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001241 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001242
Doug Zongkere5ff5902012-01-17 10:55:37 -08001243 device_specific.IncrementalOTA_InstallBegin()
1244
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001245 if OPTIONS.two_step:
1246 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1247 script.WriteRawImage("/boot", "boot.img")
1248 print "writing full boot image (forced by two-step mode)"
1249
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001250 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001251 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1252 if vendor_diff:
1253 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001254
Doug Zongker881dd402009-09-20 14:03:55 -07001255 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001256 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1257 if vendor_diff:
1258 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001259 if updating_boot:
1260 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001261
1262 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001263 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1264 if vendor_diff:
1265 script.Print("Patching vendor files...")
1266 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001267
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001268 if not OPTIONS.two_step:
1269 if updating_boot:
1270 # Produce the boot image by applying a patch to the current
1271 # contents of the boot partition, and write it back to the
1272 # partition.
1273 script.Print("Patching boot image...")
1274 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1275 % (boot_type, boot_device,
1276 source_boot.size, source_boot.sha1,
1277 target_boot.size, target_boot.sha1),
1278 "-",
1279 target_boot.size, target_boot.sha1,
1280 source_boot.sha1, "patch/boot.img.p")
1281 so_far += target_boot.size
1282 script.SetProgress(so_far / total_patch_size)
1283 print "boot image changed; including."
1284 else:
1285 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001286
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001287 system_items = ItemSet("system", "META/filesystem_config.txt")
1288 if vendor_diff:
1289 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1290
Doug Zongkereef39442009-04-02 12:14:19 -07001291 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001292 # Recovery is generated as a patch using both the boot image
1293 # (which contains the same linux kernel as recovery) and the file
1294 # /system/etc/recovery-resource.dat (which contains all the images
1295 # used in the recovery UI) as sources. This lets us minimize the
1296 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001297 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001298 # For older builds where recovery-resource.dat is not present, we
1299 # use only the boot image as the source.
1300
Doug Zongkerc9253822014-02-04 12:17:58 -08001301 if not target_has_recovery_patch:
1302 def output_sink(fn, data):
1303 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001304 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001305
1306 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1307 target_recovery, target_boot)
1308 script.DeleteFiles(["/system/recovery-from-boot.p",
1309 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001310 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001311 else:
1312 print "recovery image unchanged; skipping."
1313
Doug Zongker881dd402009-09-20 14:03:55 -07001314 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001315
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001316 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1317 if vendor_diff:
1318 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1319
1320 temp_script = script.MakeTemporary()
1321 system_items.GetMetadata(target_zip)
1322 system_items.Get("system").SetPermissions(temp_script)
1323 if vendor_diff:
1324 vendor_items.GetMetadata(target_zip)
1325 vendor_items.Get("vendor").SetPermissions(temp_script)
1326
1327 # Note that this call will mess up the trees of Items, so make sure
1328 # we're done with them.
1329 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1330 if vendor_diff:
1331 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001332
1333 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001334 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1335
1336 # Delete all the symlinks in source that aren't in target. This
1337 # needs to happen before verbatim files are unpacked, in case a
1338 # symlink in the source is replaced by a real file in the target.
1339 to_delete = []
1340 for dest, link in source_symlinks:
1341 if link not in target_symlinks_d:
1342 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001343 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001344
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001345 if system_diff.verbatim_targets:
1346 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001347 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001348 if vendor_diff and vendor_diff.verbatim_targets:
1349 script.Print("Unpacking new vendor files...")
1350 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001351
Doug Zongkerc9253822014-02-04 12:17:58 -08001352 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001353 script.Print("Unpacking new recovery...")
1354 script.UnpackPackageDir("recovery", "/system")
1355
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001356 system_diff.EmitRenames(script)
1357 if vendor_diff:
1358 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001359
Doug Zongker05d3dea2009-06-22 11:32:31 -07001360 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001361
1362 # Create all the symlinks that don't already exist, or point to
1363 # somewhere different than what we want. Delete each symlink before
1364 # creating it, since the 'symlink' command won't overwrite.
1365 to_create = []
1366 for dest, link in target_symlinks:
1367 if link in source_symlinks_d:
1368 if dest != source_symlinks_d[link]:
1369 to_create.append((dest, link))
1370 else:
1371 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001372 script.DeleteFiles([i[1] for i in to_create])
1373 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001374
1375 # Now that the symlinks are created, we can set all the
1376 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001377 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001378
Doug Zongker881dd402009-09-20 14:03:55 -07001379 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001380 device_specific.IncrementalOTA_InstallEnd()
1381
Doug Zongker1c390a22009-05-14 19:06:36 -07001382 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001383 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001384
Doug Zongkere92f15a2011-08-26 13:46:40 -07001385 # Patch the build.prop file last, so if something fails but the
1386 # device can still come up, it appears to be the old build and will
1387 # get set the OTA package again to retry.
1388 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001389 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001390
Doug Zongker922206e2014-03-04 13:16:24 -08001391 if OPTIONS.wipe_user_data:
1392 script.Print("Erasing user data...")
1393 script.FormatPartition("/data")
1394
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001395 if OPTIONS.two_step:
1396 script.AppendExtra("""
1397set_stage("%(bcb_dev)s", "");
1398endif;
1399endif;
1400""" % bcb_dev)
1401
Doug Zongker25568482014-03-03 10:21:27 -08001402 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001403 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001404
1405
1406def main(argv):
1407
1408 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001409 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001410 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001411 elif o in ("-k", "--package_key"):
1412 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001413 elif o in ("-i", "--incremental_from"):
1414 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001415 elif o in ("-w", "--wipe_user_data"):
1416 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001417 elif o in ("-n", "--no_prereq"):
1418 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001419 elif o in ("-o", "--oem_settings"):
1420 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001421 elif o in ("-e", "--extra_script"):
1422 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001423 elif o in ("-a", "--aslr_mode"):
1424 if a in ("on", "On", "true", "True", "yes", "Yes"):
1425 OPTIONS.aslr_mode = True
1426 else:
1427 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001428 elif o in ("-t", "--worker_threads"):
1429 if a.isdigit():
1430 OPTIONS.worker_threads = int(a)
1431 else:
1432 raise ValueError("Cannot parse value %r for option %r - only "
1433 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001434 elif o in ("-2", "--two_step"):
1435 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001436 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001437 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001438 elif o == "--block":
1439 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001440 elif o in ("-b", "--binary"):
1441 OPTIONS.updater_binary = a
Doug Zongkereef39442009-04-02 12:14:19 -07001442 else:
1443 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001444 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001445
1446 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001447 extra_opts="b:k:i:d:wne:t:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001448 extra_long_opts=["board_config=",
1449 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001450 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001451 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001452 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001453 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001454 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001455 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001456 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001457 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001458 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001459 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001460 "oem_settings=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001461 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001462 extra_option_handler=option_handler)
1463
1464 if len(args) != 2:
1465 common.Usage(__doc__)
1466 sys.exit(1)
1467
Doug Zongker1c390a22009-05-14 19:06:36 -07001468 if OPTIONS.extra_script is not None:
1469 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1470
Doug Zongkereef39442009-04-02 12:14:19 -07001471 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001472 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001473
Doug Zongkereef39442009-04-02 12:14:19 -07001474 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001475 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001476
1477 # If this image was originally labelled with SELinux contexts, make sure we
1478 # also apply the labels in our new image. During building, the "file_contexts"
1479 # is in the out/ directory tree, but for repacking from target-files.zip it's
1480 # in the root directory of the ramdisk.
1481 if "selinux_fc" in OPTIONS.info_dict:
1482 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1483 "file_contexts")
1484
Doug Zongker37974732010-09-16 17:44:38 -07001485 if OPTIONS.verbose:
1486 print "--- target info ---"
1487 common.DumpInfoDict(OPTIONS.info_dict)
1488
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001489 # If the caller explicitly specified the device-specific extensions
1490 # path via -s/--device_specific, use that. Otherwise, use
1491 # META/releasetools.py if it is present in the target target_files.
1492 # Otherwise, take the path of the file from 'tool_extensions' in the
1493 # info dict and look for that in the local filesystem, relative to
1494 # the current directory.
1495
Doug Zongker37974732010-09-16 17:44:38 -07001496 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001497 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1498 if os.path.exists(from_input):
1499 print "(using device-specific extensions from target_files)"
1500 OPTIONS.device_specific = from_input
1501 else:
1502 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1503
Doug Zongker37974732010-09-16 17:44:38 -07001504 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001505 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001506
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001507 if OPTIONS.no_signing:
1508 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1509 else:
1510 temp_zip_file = tempfile.NamedTemporaryFile()
1511 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1512 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001513
1514 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001515 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001516 if OPTIONS.package_key is None:
1517 OPTIONS.package_key = OPTIONS.info_dict.get(
1518 "default_system_dev_certificate",
1519 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001520 else:
1521 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001522 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001523 OPTIONS.target_info_dict = OPTIONS.info_dict
1524 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongker5fad2032014-02-24 08:13:45 -08001525 if "selinux_fc" in OPTIONS.source_info_dict:
1526 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1527 "file_contexts")
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001528 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001529 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001530 "default_system_dev_certificate",
1531 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001532 if OPTIONS.verbose:
1533 print "--- source info ---"
1534 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001535 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001536
1537 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001538
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001539 if not OPTIONS.no_signing:
1540 SignOutput(temp_zip_file.name, args[1])
1541 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001542
1543 common.Cleanup()
1544
1545 print "done."
1546
1547
1548if __name__ == '__main__':
1549 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001550 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001551 main(sys.argv[1:])
1552 except common.ExternalError, e:
1553 print
1554 print " ERROR: %s" % (e,)
1555 print
1556 sys.exit(1)