blob: b281de4a0a1c0e8133ce1a46f3cc383969944291 [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 Zongker62d4f182014-08-04 16:06:43 -0700121OPTIONS.fallback_to_full = True
Doug Zongkereef39442009-04-02 12:14:19 -0700122
123def MostPopularKey(d, default):
124 """Given a dict, return the key corresponding to the largest
125 value. Returns 'default' if the dict is empty."""
126 x = [(v, k) for (k, v) in d.iteritems()]
127 if not x: return default
128 x.sort()
129 return x[-1][1]
130
131
132def IsSymlink(info):
133 """Return true if the zipfile.ZipInfo object passed in represents a
134 symlink."""
135 return (info.external_attr >> 16) == 0120777
136
Hristo Bojinov96be7202010-08-02 10:26:17 -0700137def IsRegular(info):
138 """Return true if the zipfile.ZipInfo object passed in represents a
139 symlink."""
140 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700141
Michael Runge4038aa82013-12-13 18:06:28 -0800142def ClosestFileMatch(src, tgtfiles, existing):
143 """Returns the closest file match between a source file and list
144 of potential matches. The exact filename match is preferred,
145 then the sha1 is searched for, and finally a file with the same
146 basename is evaluated. Rename support in the updater-binary is
147 required for the latter checks to be used."""
148
149 result = tgtfiles.get("path:" + src.name)
150 if result is not None:
151 return result
152
153 if not OPTIONS.target_info_dict.get("update_rename_support", False):
154 return None
155
156 if src.size < 1000:
157 return None
158
159 result = tgtfiles.get("sha1:" + src.sha1)
160 if result is not None and existing.get(result.name) is None:
161 return result
162 result = tgtfiles.get("file:" + src.name.split("/")[-1])
163 if result is not None and existing.get(result.name) is None:
164 return result
165 return None
166
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700167class ItemSet:
168 def __init__(self, partition, fs_config):
169 self.partition = partition
170 self.fs_config = fs_config
171 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700172
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700173 def Get(self, name, dir=False):
174 if name not in self.ITEMS:
175 self.ITEMS[name] = Item(self, name, dir=dir)
176 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700177
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700178 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700179 # The target_files contains a record of what the uid,
180 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700181 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700182
183 for line in output.split("\n"):
184 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700185 columns = line.split()
186 name, uid, gid, mode = columns[:4]
187 selabel = None
188 capabilities = None
189
190 # After the first 4 columns, there are a series of key=value
191 # pairs. Extract out the fields we care about.
192 for element in columns[4:]:
193 key, value = element.split("=")
194 if key == "selabel":
195 selabel = value
196 if key == "capabilities":
197 capabilities = value
198
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700199 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700200 if i is not None:
201 i.uid = int(uid)
202 i.gid = int(gid)
203 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700204 i.selabel = selabel
205 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700206 if i.dir:
207 i.children.sort(key=lambda i: i.name)
208
209 # set metadata for the files generated by this script.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700210 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700211 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700212 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700213 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700214
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700215
216class Item:
217 """Items represent the metadata (user, group, mode) of files and
218 directories in the system image."""
219 def __init__(self, itemset, name, dir=False):
220 self.itemset = itemset
221 self.name = name
222 self.uid = None
223 self.gid = None
224 self.mode = None
225 self.selabel = None
226 self.capabilities = None
227 self.dir = dir
228
229 if name:
230 self.parent = itemset.Get(os.path.dirname(name), dir=True)
231 self.parent.children.append(self)
232 else:
233 self.parent = None
234 if dir:
235 self.children = []
236
237 def Dump(self, indent=0):
238 if self.uid is not None:
239 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
240 else:
241 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
242 if self.dir:
243 print "%s%s" % (" "*indent, self.descendants)
244 print "%s%s" % (" "*indent, self.best_subtree)
245 for i in self.children:
246 i.Dump(indent=indent+1)
247
Doug Zongkereef39442009-04-02 12:14:19 -0700248 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700249 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
250 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700251 set_perm to correctly chown/chmod all the files to their desired
252 values. Recursively calls itself for all descendants.
253
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700254 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700255 all descendants of this node. (dmode or fmode may be None.) Also
256 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 dmode, fmode, selabel, capabilities) tuple that will match the most
258 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700259 """
260
261 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700262 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700263 for i in self.children:
264 if i.dir:
265 for k, v in i.CountChildMetadata().iteritems():
266 d[k] = d.get(k, 0) + v
267 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700268 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700269 d[k] = d.get(k, 0) + 1
270
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700271 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
272 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700273
274 # First, find the (uid, gid) pair that matches the most
275 # descendants.
276 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700277 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700278 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
279 ug = MostPopularKey(ug, (0, 0))
280
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700281 # Now find the dmode, fmode, selabel, and capabilities that match
282 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700283 best_dmode = (0, 0755)
284 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700285 best_selabel = (0, None)
286 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700287 for k, count in d.iteritems():
288 if k[:2] != ug: continue
289 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
290 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700291 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
292 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
293 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700294
295 return d
296
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700297 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700298 """Append set_perm/set_perm_recursive commands to 'script' to
299 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700300 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700301
302 self.CountChildMetadata()
303
304 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700305 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700306 # item (and all its children) have already been set to. We only
307 # need to issue set_perm/set_perm_recursive commands if we're
308 # supposed to be something different.
309 if item.dir:
310 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700311 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700312 current = item.best_subtree
313
314 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700315 item.mode != current[2] or item.selabel != current[4] or \
316 item.capabilities != current[5]:
317 script.SetPermissions("/"+item.name, item.uid, item.gid,
318 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700319
320 for i in item.children:
321 recurse(i, current)
322 else:
323 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700324 item.mode != current[3] or item.selabel != current[4] or \
325 item.capabilities != current[5]:
326 script.SetPermissions("/"+item.name, item.uid, item.gid,
327 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700328
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700329 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700330
331
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700332def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
333 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700334 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800335 list of symlinks. output_zip may be None, in which case the copy is
336 skipped (but the other side effects still happen). substitute is an
337 optional dict of {output filename: contents} to be output instead of
338 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700339 """
340
341 symlinks = []
342
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700343 partition = itemset.partition
344
Doug Zongkereef39442009-04-02 12:14:19 -0700345 for info in input_zip.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700346 if info.filename.startswith(partition.upper() + "/"):
Doug Zongkereef39442009-04-02 12:14:19 -0700347 basefilename = info.filename[7:]
348 if IsSymlink(info):
349 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700350 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700351 else:
352 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700353 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700354 if substitute and fn in substitute and substitute[fn] is None:
355 continue
356 if output_zip is not None:
357 if substitute and fn in substitute:
358 data = substitute[fn]
359 else:
360 data = input_zip.read(info.filename)
361 output_zip.writestr(info2, data)
362 if fn.endswith("/"):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700363 itemset.Get(fn[:-1], dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700364 else:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700365 itemset.Get(fn, dir=False)
Doug Zongkereef39442009-04-02 12:14:19 -0700366
367 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800368 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700369
370
Doug Zongkereef39442009-04-02 12:14:19 -0700371def SignOutput(temp_zip_name, output_zip_name):
372 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
373 pw = key_passwords[OPTIONS.package_key]
374
Doug Zongker951495f2009-08-14 12:44:19 -0700375 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
376 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700377
378
Michael Rungec6e3afd2014-05-05 11:55:47 -0700379def AppendAssertions(script, info_dict, oem_dict = None):
Michael Runge6e836112014-04-15 17:40:21 -0700380 oem_props = info_dict.get("oem_fingerprint_properties")
381 if oem_props is None:
382 device = GetBuildProp("ro.product.device", info_dict)
383 script.AssertDevice(device)
384 else:
385 if oem_dict is None:
386 raise common.ExternalError("No OEM file provided to answer expected assertions")
387 for prop in oem_props.split():
388 if oem_dict.get(prop) is None:
389 raise common.ExternalError("The OEM file is missing the property %s" % prop)
390 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700391
Doug Zongkereef39442009-04-02 12:14:19 -0700392
Doug Zongkerc9253822014-02-04 12:17:58 -0800393def HasRecoveryPatch(target_files_zip):
394 try:
395 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
396 return True
397 except KeyError:
398 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700399
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700400def HasVendorPartition(target_files_zip):
401 try:
402 target_files_zip.getinfo("VENDOR/")
403 return True
404 except KeyError:
405 return False
406
Michael Runge6e836112014-04-15 17:40:21 -0700407def GetOemProperty(name, oem_props, oem_dict, info_dict):
408 if oem_props is not None and name in oem_props:
409 return oem_dict[name]
410 return GetBuildProp(name, info_dict)
411
412
413def CalculateFingerprint(oem_props, oem_dict, info_dict):
414 if oem_props is None:
415 return GetBuildProp("ro.build.fingerprint", info_dict)
416 return "%s/%s/%s:%s" % (
417 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
418 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
419 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
420 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700421
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700422def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700423 # TODO: how to determine this? We don't know what version it will
424 # be installed on top of. For now, we expect the API just won't
425 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700426 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700427
Michael Runge6e836112014-04-15 17:40:21 -0700428 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
429 oem_dict = None
430 if oem_props is not None:
431 if OPTIONS.oem_source is None:
432 raise common.ExternalError("OEM source required for this build")
433 script.Mount("/oem")
434 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
435
436 metadata = {"post-build": CalculateFingerprint(
437 oem_props, oem_dict, OPTIONS.info_dict),
438 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700439 OPTIONS.info_dict),
440 "post-timestamp": GetBuildProp("ro.build.date.utc",
441 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700442 }
443
Doug Zongker05d3dea2009-06-22 11:32:31 -0700444 device_specific = common.DeviceSpecificParams(
445 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700446 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700447 output_zip=output_zip,
448 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700449 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700450 metadata=metadata,
451 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700452
Doug Zongkerc9253822014-02-04 12:17:58 -0800453 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800454 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800455
Doug Zongker962069c2009-04-23 11:41:58 -0700456 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700457 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700458 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
459 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700460
Michael Runge6e836112014-04-15 17:40:21 -0700461 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700462 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800463
464 # Two-step package strategy (in chronological order, which is *not*
465 # the order in which the generated script has things):
466 #
467 # if stage is not "2/3" or "3/3":
468 # write recovery image to boot partition
469 # set stage to "2/3"
470 # reboot to boot partition and restart recovery
471 # else if stage is "2/3":
472 # write recovery image to recovery partition
473 # set stage to "3/3"
474 # reboot to recovery partition and restart recovery
475 # else:
476 # (stage must be "3/3")
477 # set stage to ""
478 # do normal full package installation:
479 # wipe and install system, boot image, etc.
480 # set up system to update recovery partition on first boot
481 # complete script normally (allow recovery to mark itself finished and reboot)
482
483 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
484 OPTIONS.input_tmp, "RECOVERY")
485 if OPTIONS.two_step:
486 if not OPTIONS.info_dict.get("multistage_support", None):
487 assert False, "two-step packages not supported by this build"
488 fs = OPTIONS.info_dict["fstab"]["/misc"]
489 assert fs.fs_type.upper() == "EMMC", \
490 "two-step packages only supported on devices with EMMC /misc partitions"
491 bcb_dev = {"bcb_dev": fs.device}
492 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
493 script.AppendExtra("""
494if get_stage("%(bcb_dev)s", "stage") == "2/3" then
495""" % bcb_dev)
496 script.WriteRawImage("/recovery", "recovery.img")
497 script.AppendExtra("""
498set_stage("%(bcb_dev)s", "3/3");
499reboot_now("%(bcb_dev)s", "recovery");
500else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
501""" % bcb_dev)
502
Doug Zongkere5ff5902012-01-17 10:55:37 -0800503 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700504
Doug Zongker01ce19c2014-02-04 13:48:15 -0800505 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700506
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700507 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800508 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700509 if HasVendorPartition(input_zip):
510 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700511
Kenny Rootf32dc712012-04-08 10:42:34 -0700512 if "selinux_fc" in OPTIONS.info_dict:
513 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500514
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700515 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700516 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800517 if block_based:
Doug Zongker5fad2032014-02-24 08:13:45 -0800518 mapdata, data = img_from_target_files.BuildSystem(
519 OPTIONS.input_tmp, OPTIONS.info_dict,
520 sparse=False, map_file=True)
521
522 common.ZipWriteStr(output_zip, "system.map", mapdata)
523 common.ZipWriteStr(output_zip, "system.muimg", data)
524 script.WipeBlockDevice("/system")
525 script.WriteRawImage("/system", "system.muimg", mapfn="system.map")
Doug Zongker01ce19c2014-02-04 13:48:15 -0800526 else:
527 script.FormatPartition("/system")
528 script.Mount("/system")
529 if not has_recovery_patch:
530 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800531 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700532
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700533 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800534 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700535
Doug Zongker55d93282011-01-25 17:03:34 -0800536 boot_img = common.GetBootableImage("boot.img", "boot.img",
537 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800538
Doug Zongker91a99c22014-05-09 13:15:01 -0700539 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800540 def output_sink(fn, data):
541 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700542 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800543
544 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
545 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700546
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700547 system_items.GetMetadata(input_zip)
548 system_items.Get("system").SetPermissions(script)
549
550 if HasVendorPartition(input_zip):
551 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
552 script.ShowProgress(0.1, 0)
553
554 if block_based:
555 mapdata, data = img_from_target_files.BuildVendor(
556 OPTIONS.input_tmp, OPTIONS.info_dict,
557 sparse=False, map_file=True)
558
559 common.ZipWriteStr(output_zip, "vendor.map", mapdata)
560 common.ZipWriteStr(output_zip, "vendor.muimg", data)
561 script.WipeBlockDevice("/vendor")
562 script.WriteRawImage("/vendor", "vendor.muimg", mapfn="vendor.map")
563 else:
564 script.FormatPartition("/vendor")
565 script.Mount("/vendor")
566 script.UnpackPackageDir("vendor", "/vendor")
567
568 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
569 script.MakeSymlinks(symlinks)
570
571 vendor_items.GetMetadata(input_zip)
572 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700573
Doug Zongker37974732010-09-16 17:44:38 -0700574 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700575 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700576
Doug Zongker01ce19c2014-02-04 13:48:15 -0800577 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700578 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700579
Doug Zongker01ce19c2014-02-04 13:48:15 -0800580 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700581 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700582
Doug Zongker1c390a22009-05-14 19:06:36 -0700583 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700584 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700585
Doug Zongker14833602010-02-02 13:12:04 -0800586 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800587
Doug Zongker922206e2014-03-04 13:16:24 -0800588 if OPTIONS.wipe_user_data:
589 script.ShowProgress(0.1, 10)
590 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700591
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800592 if OPTIONS.two_step:
593 script.AppendExtra("""
594set_stage("%(bcb_dev)s", "");
595""" % bcb_dev)
596 script.AppendExtra("else\n")
597 script.WriteRawImage("/boot", "recovery.img")
598 script.AppendExtra("""
599set_stage("%(bcb_dev)s", "2/3");
600reboot_now("%(bcb_dev)s", "");
601endif;
602endif;
603""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800604 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700605 WriteMetadata(metadata, output_zip)
606
Stephen Smalley56882bf2012-02-09 13:36:21 -0500607def WritePolicyConfig(file_context, output_zip):
608 f = open(file_context, 'r');
609 basename = os.path.basename(file_context)
610 common.ZipWriteStr(output_zip, basename, f.read())
611
Doug Zongker2ea21062010-04-28 16:05:21 -0700612
613def WriteMetadata(metadata, output_zip):
614 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
615 "".join(["%s=%s\n" % kv
616 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700617
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700618def LoadPartitionFiles(z, partition):
619 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700620 ZipFile, and return a dict of {filename: File object}."""
621 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700622 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700623 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700624 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700625 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700626 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700627 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700628 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800629 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700630
631
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700632def GetBuildProp(prop, info_dict):
633 """Return the fingerprint of the build of a given target-files info_dict."""
634 try:
635 return info_dict.get("build.prop", {})[prop]
636 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700637 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700638
Michael Runge4038aa82013-12-13 18:06:28 -0800639def AddToKnownPaths(filename, known_paths):
640 if filename[-1] == "/":
641 return
642 dirs = filename.split("/")[:-1]
643 while len(dirs) > 0:
644 path = "/".join(dirs)
645 if path in known_paths:
646 break;
647 known_paths.add(path)
648 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700649
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700650class BlockDifference:
651 def __init__(self, partition, builder, output_zip):
652 with tempfile.NamedTemporaryFile() as src_file:
653 with tempfile.NamedTemporaryFile() as tgt_file:
654 print "building source " + partition + " image..."
655 src_file = tempfile.NamedTemporaryFile()
656 src_mapdata, src_data = builder(OPTIONS.source_tmp,
657 OPTIONS.source_info_dict,
658 sparse=False, map_file=True)
659
660 self.src_sha1 = sha1(src_data).hexdigest()
661 print "source " + partition + " sha1:", self.src_sha1
662 src_file.write(src_data)
663
664 print "building target " + partition + " image..."
665 tgt_file = tempfile.NamedTemporaryFile()
666 tgt_mapdata, tgt_data = builder(OPTIONS.target_tmp,
667 OPTIONS.target_info_dict,
668 sparse=False, map_file=True)
669 self.tgt_sha1 = sha1(tgt_data).hexdigest()
670 print "target " + partition + " sha1:", self.tgt_sha1
671 tgt_len = len(tgt_data)
672 tgt_file.write(tgt_data)
673
674 system_type, self.device = common.GetTypeAndDevice("/" + partition,
675 OPTIONS.info_dict)
676 self.patch = common.MakePartitionPatch(src_file, tgt_file, partition)
677
678 TestBlockPatch(src_data, src_mapdata, self.patch.data,
679 tgt_mapdata, self.tgt_sha1)
680 src_data = None
681 tgt_data = None
682
683 self.patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
684 self.src_mapfilename = self.patch.name + ".src.map"
685 common.ZipWriteStr(output_zip, self.src_mapfilename, src_mapdata)
686 self.tgt_mapfilename = self.patch.name + ".tgt.map"
687 common.ZipWriteStr(output_zip, self.tgt_mapfilename, tgt_mapdata)
688
Geremy Condra36bd3652014-02-06 19:45:10 -0800689def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
690 source_version = OPTIONS.source_info_dict["recovery_api_version"]
691 target_version = OPTIONS.target_info_dict["recovery_api_version"]
692
693 if source_version == 0:
694 print ("WARNING: generating edify script for a source that "
695 "can't install it.")
696 script = edify_generator.EdifyGenerator(source_version,
697 OPTIONS.target_info_dict)
698
699 metadata = {"pre-device": GetBuildProp("ro.product.device",
700 OPTIONS.source_info_dict),
701 "post-timestamp": GetBuildProp("ro.build.date.utc",
702 OPTIONS.target_info_dict),
703 }
704
705 device_specific = common.DeviceSpecificParams(
706 source_zip=source_zip,
707 source_version=source_version,
708 target_zip=target_zip,
709 target_version=target_version,
710 output_zip=output_zip,
711 script=script,
712 metadata=metadata,
713 info_dict=OPTIONS.info_dict)
714
715 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
716 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
717 metadata["pre-build"] = source_fp
718 metadata["post-build"] = target_fp
719
720 source_boot = common.GetBootableImage(
721 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
722 OPTIONS.source_info_dict)
723 target_boot = common.GetBootableImage(
724 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
725 updating_boot = (not OPTIONS.two_step and
726 (source_boot.data != target_boot.data))
727
728 source_recovery = common.GetBootableImage(
729 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
730 OPTIONS.source_info_dict)
731 target_recovery = common.GetBootableImage(
732 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
733 updating_recovery = (source_recovery.data != target_recovery.data)
734
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700735 system_diff = BlockDifference("system", img_from_target_files.BuildSystem,
736 output_zip)
737 if HasVendorPartition(target_zip):
738 if not HasVendorPartition(source_zip):
739 raise RuntimeError("can't generate incremental that adds /vendor")
740 vendor_diff = BlockDifference("vendor", img_from_target_files.BuildVendor,
741 output_zip)
Geremy Condra36bd3652014-02-06 19:45:10 -0800742
Michael Rungec6e3afd2014-05-05 11:55:47 -0700743 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
744 oem_dict = None
745 if oem_props is not None:
746 if OPTIONS.oem_source is None:
747 raise common.ExternalError("OEM source required for this build")
748 script.Mount("/oem")
749 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
750
751 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800752 device_specific.IncrementalOTA_Assertions()
753
754 # Two-step incremental package strategy (in chronological order,
755 # which is *not* the order in which the generated script has
756 # things):
757 #
758 # if stage is not "2/3" or "3/3":
759 # do verification on current system
760 # write recovery image to boot partition
761 # set stage to "2/3"
762 # reboot to boot partition and restart recovery
763 # else if stage is "2/3":
764 # write recovery image to recovery partition
765 # set stage to "3/3"
766 # reboot to recovery partition and restart recovery
767 # else:
768 # (stage must be "3/3")
769 # perform update:
770 # patch system files, etc.
771 # force full install of new boot image
772 # set up system to update recovery partition on first boot
773 # complete script normally (allow recovery to mark itself finished and reboot)
774
775 if OPTIONS.two_step:
776 if not OPTIONS.info_dict.get("multistage_support", None):
777 assert False, "two-step packages not supported by this build"
778 fs = OPTIONS.info_dict["fstab"]["/misc"]
779 assert fs.fs_type.upper() == "EMMC", \
780 "two-step packages only supported on devices with EMMC /misc partitions"
781 bcb_dev = {"bcb_dev": fs.device}
782 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
783 script.AppendExtra("""
784if get_stage("%(bcb_dev)s", "stage") == "2/3" then
785""" % bcb_dev)
786 script.AppendExtra("sleep(20);\n");
787 script.WriteRawImage("/recovery", "recovery.img")
788 script.AppendExtra("""
789set_stage("%(bcb_dev)s", "3/3");
790reboot_now("%(bcb_dev)s", "recovery");
791else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
792""" % bcb_dev)
793
794 script.Print("Verifying current system...")
795
796 device_specific.IncrementalOTA_VerifyBegin()
797
Michael Rungec6e3afd2014-05-05 11:55:47 -0700798 if oem_props is None:
799 script.AssertSomeFingerprint(source_fp, target_fp)
800 else:
801 script.AssertSomeThumbprint(
802 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
803 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800804
805 if updating_boot:
Geremy Condra36bd3652014-02-06 19:45:10 -0800806 d = common.Difference(target_boot, source_boot)
807 _, _, d = d.ComputePatch()
808 print "boot target: %d source: %d diff: %d" % (
809 target_boot.size, source_boot.size, len(d))
810
811 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
812
813 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
814
815 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
816 (boot_type, boot_device,
817 source_boot.size, source_boot.sha1,
818 target_boot.size, target_boot.sha1))
819
820 device_specific.IncrementalOTA_VerifyEnd()
821
822 if OPTIONS.two_step:
823 script.WriteRawImage("/boot", "recovery.img")
824 script.AppendExtra("""
825set_stage("%(bcb_dev)s", "2/3");
826reboot_now("%(bcb_dev)s", "");
827else
828""" % bcb_dev)
829
830 script.Comment("---- start making changes here ----")
831
832 device_specific.IncrementalOTA_InstallBegin()
833
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700834 if HasVendorPartition(target_zip):
835 script.Print("Patching vendor image...")
836 script.ShowProgress(0.1, 0)
837 script.Syspatch(vendor_diff.device,
838 vendor_diff.tgt_mapfilename, vendor_diff.tgt_sha1,
839 vendor_diff.src_mapfilename, vendor_diff.src_sha1,
840 vendor_diff.patch.name)
841 sys_progress = 0.8
842 else:
843 sys_progress = 0.9
844
Geremy Condra36bd3652014-02-06 19:45:10 -0800845 script.Print("Patching system image...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700846 script.ShowProgress(sys_progress, 0)
847 script.Syspatch(system_diff.device,
848 system_diff.tgt_mapfilename, system_diff.tgt_sha1,
849 system_diff.src_mapfilename, system_diff.src_sha1,
850 system_diff.patch.name)
Geremy Condra36bd3652014-02-06 19:45:10 -0800851
852 if OPTIONS.two_step:
853 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
854 script.WriteRawImage("/boot", "boot.img")
855 print "writing full boot image (forced by two-step mode)"
856
857 if not OPTIONS.two_step:
858 if updating_boot:
859 # Produce the boot image by applying a patch to the current
860 # contents of the boot partition, and write it back to the
861 # partition.
862 script.Print("Patching boot image...")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700863 script.ShowProgress(0.1, 10)
Geremy Condra36bd3652014-02-06 19:45:10 -0800864 script.ApplyPatch("%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 target_boot.size, target_boot.sha1,
870 source_boot.sha1, "patch/boot.img.p")
871 print "boot image changed; including."
872 else:
873 print "boot image unchanged; skipping."
874
875 # Do device-specific installation (eg, write radio image).
876 device_specific.IncrementalOTA_InstallEnd()
877
878 if OPTIONS.extra_script is not None:
879 script.AppendExtra(OPTIONS.extra_script)
880
Doug Zongker922206e2014-03-04 13:16:24 -0800881 if OPTIONS.wipe_user_data:
882 script.Print("Erasing user data...")
883 script.FormatPartition("/data")
884
Geremy Condra36bd3652014-02-06 19:45:10 -0800885 if OPTIONS.two_step:
886 script.AppendExtra("""
887set_stage("%(bcb_dev)s", "");
888endif;
889endif;
890""" % bcb_dev)
891
892 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800893 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800894 WriteMetadata(metadata, output_zip)
895
Doug Zongker32b527d2014-03-04 10:03:02 -0800896def ParseMap(map_str):
897 x = map_str.split()
898 assert int(x[0]) == 4096
899 assert int(x[1]) == len(x)-2
900 return int(x[0]), [int(i) for i in x[2:]]
901
902def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1):
903 src_blksize, src_regions = ParseMap(src_map)
904 tgt_blksize, tgt_regions = ParseMap(tgt_map)
905
906 with tempfile.NamedTemporaryFile() as src_file,\
907 tempfile.NamedTemporaryFile() as patch_file,\
Doug Zongker32b527d2014-03-04 10:03:02 -0800908 tempfile.NamedTemporaryFile() as src_map_file,\
909 tempfile.NamedTemporaryFile() as tgt_map_file:
910
911 src_total = sum(src_regions) * src_blksize
912 src_file.truncate(src_total)
913 p = 0
914 for i in range(0, len(src_regions), 2):
915 c, dc = src_regions[i:i+2]
916 src_file.write(src_muimg[p:(p+c*src_blksize)])
917 p += c*src_blksize
918 src_file.seek(dc*src_blksize, 1)
919 assert src_file.tell() == src_total
920
921 patch_file.write(patch_data)
922
Doug Zongker32b527d2014-03-04 10:03:02 -0800923 src_map_file.write(src_map)
924 tgt_map_file.write(tgt_map)
925
926 src_file.flush()
927 src_map_file.flush()
928 patch_file.flush()
Doug Zongker32b527d2014-03-04 10:03:02 -0800929 tgt_map_file.flush()
930
931 p = common.Run(["syspatch_host", src_file.name, src_map_file.name,
Doug Zongker1113e382014-06-13 10:38:32 -0700932 patch_file.name, src_file.name, tgt_map_file.name],
Doug Zongker32b527d2014-03-04 10:03:02 -0800933 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
934 stdoutdata, _ = p.communicate()
935 if p.returncode != 0:
936 print stdoutdata
937 raise ValueError("failed to reconstruct target system image from patch")
938
939 h = sha1()
Doug Zongker1113e382014-06-13 10:38:32 -0700940 src_file.seek(0, 0)
Doug Zongker32b527d2014-03-04 10:03:02 -0800941 for i in range(0, len(tgt_regions), 2):
942 c, dc = tgt_regions[i:i+2]
Doug Zongker1113e382014-06-13 10:38:32 -0700943 h.update(src_file.read(c*tgt_blksize))
944 src_file.seek(dc*tgt_blksize, 1)
Doug Zongker32b527d2014-03-04 10:03:02 -0800945
946 if h.hexdigest() != tgt_sha1:
947 raise ValueError("patch reconstructed incorrect target system image")
948
949 print "test of system image patch succeeded"
950
951
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700952class FileDifference:
953 def __init__(self, partition, source_zip, target_zip, output_zip):
954 print "Loading target..."
955 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
956 print "Loading source..."
957 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
958
959 self.verbatim_targets = verbatim_targets = []
960 self.patch_list = patch_list = []
961 diffs = []
962 self.renames = renames = {}
963 known_paths = set()
964 largest_source_size = 0
965
966 matching_file_cache = {}
967 for fn, sf in source_data.items():
968 assert fn == sf.name
969 matching_file_cache["path:" + fn] = sf
970 if fn in target_data.keys():
971 AddToKnownPaths(fn, known_paths)
972 # Only allow eligibility for filename/sha matching
973 # if there isn't a perfect path match.
974 if target_data.get(sf.name) is None:
975 matching_file_cache["file:" + fn.split("/")[-1]] = sf
976 matching_file_cache["sha:" + sf.sha1] = sf
977
978 for fn in sorted(target_data.keys()):
979 tf = target_data[fn]
980 assert fn == tf.name
981 sf = ClosestFileMatch(tf, matching_file_cache, renames)
982 if sf is not None and sf.name != tf.name:
983 print "File has moved from " + sf.name + " to " + tf.name
984 renames[sf.name] = tf
985
986 if sf is None or fn in OPTIONS.require_verbatim:
987 # This file should be included verbatim
988 if fn in OPTIONS.prohibit_verbatim:
989 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
990 print "send", fn, "verbatim"
991 tf.AddToZip(output_zip)
992 verbatim_targets.append((fn, tf.size))
993 if fn in target_data.keys():
994 AddToKnownPaths(fn, known_paths)
995 elif tf.sha1 != sf.sha1:
996 # File is different; consider sending as a patch
997 diffs.append(common.Difference(tf, sf))
998 else:
999 # Target file data identical to source (may still be renamed)
1000 pass
1001
1002 common.ComputeDifferences(diffs)
1003
1004 for diff in diffs:
1005 tf, sf, d = diff.GetPatch()
1006 path = "/".join(tf.name.split("/")[:-1])
1007 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1008 path not in known_paths:
1009 # patch is almost as big as the file; don't bother patching
1010 # or a patch + rename cannot take place due to the target
1011 # directory not existing
1012 tf.AddToZip(output_zip)
1013 verbatim_targets.append((tf.name, tf.size))
1014 if sf.name in renames:
1015 del renames[sf.name]
1016 AddToKnownPaths(tf.name, known_paths)
1017 else:
1018 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1019 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1020 largest_source_size = max(largest_source_size, sf.size)
1021
1022 self.largest_source_size = largest_source_size
1023
1024 def EmitVerification(self, script):
1025 so_far = 0
1026 for tf, sf, size, patch_sha in self.patch_list:
1027 if tf.name != sf.name:
1028 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1029 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1030 so_far += sf.size
1031 return so_far
1032
1033 def RemoveUnneededFiles(self, script, extras=()):
1034 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
1035 ["/"+i for i in sorted(self.source_data)
1036 if i not in self.target_data and
1037 i not in self.renames] +
1038 list(extras))
1039
1040 def TotalPatchSize(self):
1041 return sum(i[1].size for i in self.patch_list)
1042
1043 def EmitPatches(self, script, total_patch_size, so_far):
1044 self.deferred_patch_list = deferred_patch_list = []
1045 for item in self.patch_list:
1046 tf, sf, size, _ = item
1047 if tf.name == "system/build.prop":
1048 deferred_patch_list.append(item)
1049 continue
1050 if (sf.name != tf.name):
1051 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1052 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1053 so_far += tf.size
1054 script.SetProgress(so_far / total_patch_size)
1055 return so_far
1056
1057 def EmitDeferredPatches(self, script):
1058 for item in self.deferred_patch_list:
1059 tf, sf, size, _ = item
1060 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1061 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1062
1063 def EmitRenames(self, script):
1064 if len(self.renames) > 0:
1065 script.Print("Renaming files...")
1066 for src, tgt in self.renames.iteritems():
1067 print "Renaming " + src + " to " + tgt.name
1068 script.RenameFile(src, tgt.name)
1069
1070
1071
1072
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001073def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001074 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1075 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1076
Doug Zongker26e66192014-02-20 13:22:07 -08001077 if (OPTIONS.block_based and
1078 target_has_recovery_patch and
1079 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001080 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1081
Doug Zongker37974732010-09-16 17:44:38 -07001082 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1083 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001084
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001085 if source_version == 0:
1086 print ("WARNING: generating edify script for a source that "
1087 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001088 script = edify_generator.EdifyGenerator(source_version,
1089 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001090
Michael Runge6e836112014-04-15 17:40:21 -07001091 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1092 oem_dict = None
1093 if oem_props is not None:
1094 if OPTIONS.oem_source is None:
1095 raise common.ExternalError("OEM source required for this build")
1096 script.Mount("/oem")
1097 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1098
1099 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001100 OPTIONS.source_info_dict),
1101 "post-timestamp": GetBuildProp("ro.build.date.utc",
1102 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001103 }
1104
Doug Zongker05d3dea2009-06-22 11:32:31 -07001105 device_specific = common.DeviceSpecificParams(
1106 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001107 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001108 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001109 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001110 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001111 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001112 metadata=metadata,
1113 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001114
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001115 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge6e836112014-04-15 17:40:21 -07001116 script.Mount("/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001117 if HasVendorPartition(target_zip):
1118 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1119 script.Mount("/vendor")
1120 else:
1121 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001122
1123 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1124 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1125
1126 if oem_props is None:
1127 script.AssertSomeFingerprint(source_fp, target_fp)
1128 else:
1129 script.AssertSomeThumbprint(
1130 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1131 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1132
Doug Zongker2ea21062010-04-28 16:05:21 -07001133 metadata["pre-build"] = source_fp
1134 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001135
Doug Zongker55d93282011-01-25 17:03:34 -08001136 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001137 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1138 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001139 target_boot = common.GetBootableImage(
1140 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001141 updating_boot = (not OPTIONS.two_step and
1142 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001143
Doug Zongker55d93282011-01-25 17:03:34 -08001144 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001145 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1146 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001147 target_recovery = common.GetBootableImage(
1148 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001149 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001150
Doug Zongker881dd402009-09-20 14:03:55 -07001151 # Here's how we divide up the progress bar:
1152 # 0.1 for verifying the start state (PatchCheck calls)
1153 # 0.8 for applying patches (ApplyPatch calls)
1154 # 0.1 for unpacking verbatim files, symlinking, and doing the
1155 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001156
Michael Runge6e836112014-04-15 17:40:21 -07001157 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001158 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001159
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001160 # Two-step incremental package strategy (in chronological order,
1161 # which is *not* the order in which the generated script has
1162 # things):
1163 #
1164 # if stage is not "2/3" or "3/3":
1165 # do verification on current system
1166 # write recovery image to boot partition
1167 # set stage to "2/3"
1168 # reboot to boot partition and restart recovery
1169 # else if stage is "2/3":
1170 # write recovery image to recovery partition
1171 # set stage to "3/3"
1172 # reboot to recovery partition and restart recovery
1173 # else:
1174 # (stage must be "3/3")
1175 # perform update:
1176 # patch system files, etc.
1177 # force full install of new boot image
1178 # set up system to update recovery partition on first boot
1179 # complete script normally (allow recovery to mark itself finished and reboot)
1180
1181 if OPTIONS.two_step:
1182 if not OPTIONS.info_dict.get("multistage_support", None):
1183 assert False, "two-step packages not supported by this build"
1184 fs = OPTIONS.info_dict["fstab"]["/misc"]
1185 assert fs.fs_type.upper() == "EMMC", \
1186 "two-step packages only supported on devices with EMMC /misc partitions"
1187 bcb_dev = {"bcb_dev": fs.device}
1188 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1189 script.AppendExtra("""
1190if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1191""" % bcb_dev)
1192 script.AppendExtra("sleep(20);\n");
1193 script.WriteRawImage("/recovery", "recovery.img")
1194 script.AppendExtra("""
1195set_stage("%(bcb_dev)s", "3/3");
1196reboot_now("%(bcb_dev)s", "recovery");
1197else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1198""" % bcb_dev)
1199
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001200 script.Print("Verifying current system...")
1201
Doug Zongkere5ff5902012-01-17 10:55:37 -08001202 device_specific.IncrementalOTA_VerifyBegin()
1203
Doug Zongker881dd402009-09-20 14:03:55 -07001204 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001205 so_far = system_diff.EmitVerification(script)
1206 if vendor_diff:
1207 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001208
Doug Zongker5da317e2009-06-02 13:38:17 -07001209 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001210 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001211 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001212 print "boot target: %d source: %d diff: %d" % (
1213 target_boot.size, source_boot.size, len(d))
1214
Doug Zongker048e7ca2009-06-15 14:31:53 -07001215 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001216
Doug Zongker96a57e72010-09-26 14:57:41 -07001217 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001218
1219 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1220 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001221 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001222 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001223 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001224
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001225 size = []
1226 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1227 if vendor_diff:
1228 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1229 if size or updating_recovery or updating_boot:
1230 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001231
Doug Zongker05d3dea2009-06-22 11:32:31 -07001232 device_specific.IncrementalOTA_VerifyEnd()
1233
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001234 if OPTIONS.two_step:
1235 script.WriteRawImage("/boot", "recovery.img")
1236 script.AppendExtra("""
1237set_stage("%(bcb_dev)s", "2/3");
1238reboot_now("%(bcb_dev)s", "");
1239else
1240""" % bcb_dev)
1241
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001242 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001243
Doug Zongkere5ff5902012-01-17 10:55:37 -08001244 device_specific.IncrementalOTA_InstallBegin()
1245
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001246 if OPTIONS.two_step:
1247 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1248 script.WriteRawImage("/boot", "boot.img")
1249 print "writing full boot image (forced by two-step mode)"
1250
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001251 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001252 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1253 if vendor_diff:
1254 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001255
Doug Zongker881dd402009-09-20 14:03:55 -07001256 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001257 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1258 if vendor_diff:
1259 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001260 if updating_boot:
1261 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001262
1263 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001264 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1265 if vendor_diff:
1266 script.Print("Patching vendor files...")
1267 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001268
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001269 if not OPTIONS.two_step:
1270 if updating_boot:
1271 # Produce the boot image by applying a patch to the current
1272 # contents of the boot partition, and write it back to the
1273 # partition.
1274 script.Print("Patching boot image...")
1275 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1276 % (boot_type, boot_device,
1277 source_boot.size, source_boot.sha1,
1278 target_boot.size, target_boot.sha1),
1279 "-",
1280 target_boot.size, target_boot.sha1,
1281 source_boot.sha1, "patch/boot.img.p")
1282 so_far += target_boot.size
1283 script.SetProgress(so_far / total_patch_size)
1284 print "boot image changed; including."
1285 else:
1286 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001287
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001288 system_items = ItemSet("system", "META/filesystem_config.txt")
1289 if vendor_diff:
1290 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1291
Doug Zongkereef39442009-04-02 12:14:19 -07001292 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001293 # Recovery is generated as a patch using both the boot image
1294 # (which contains the same linux kernel as recovery) and the file
1295 # /system/etc/recovery-resource.dat (which contains all the images
1296 # used in the recovery UI) as sources. This lets us minimize the
1297 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001298 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001299 # For older builds where recovery-resource.dat is not present, we
1300 # use only the boot image as the source.
1301
Doug Zongkerc9253822014-02-04 12:17:58 -08001302 if not target_has_recovery_patch:
1303 def output_sink(fn, data):
1304 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001305 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001306
1307 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1308 target_recovery, target_boot)
1309 script.DeleteFiles(["/system/recovery-from-boot.p",
1310 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001311 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001312 else:
1313 print "recovery image unchanged; skipping."
1314
Doug Zongker881dd402009-09-20 14:03:55 -07001315 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001316
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001317 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1318 if vendor_diff:
1319 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1320
1321 temp_script = script.MakeTemporary()
1322 system_items.GetMetadata(target_zip)
1323 system_items.Get("system").SetPermissions(temp_script)
1324 if vendor_diff:
1325 vendor_items.GetMetadata(target_zip)
1326 vendor_items.Get("vendor").SetPermissions(temp_script)
1327
1328 # Note that this call will mess up the trees of Items, so make sure
1329 # we're done with them.
1330 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1331 if vendor_diff:
1332 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001333
1334 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001335 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1336
1337 # Delete all the symlinks in source that aren't in target. This
1338 # needs to happen before verbatim files are unpacked, in case a
1339 # symlink in the source is replaced by a real file in the target.
1340 to_delete = []
1341 for dest, link in source_symlinks:
1342 if link not in target_symlinks_d:
1343 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001344 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001345
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001346 if system_diff.verbatim_targets:
1347 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001348 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001349 if vendor_diff and vendor_diff.verbatim_targets:
1350 script.Print("Unpacking new vendor files...")
1351 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001352
Doug Zongkerc9253822014-02-04 12:17:58 -08001353 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001354 script.Print("Unpacking new recovery...")
1355 script.UnpackPackageDir("recovery", "/system")
1356
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001357 system_diff.EmitRenames(script)
1358 if vendor_diff:
1359 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001360
Doug Zongker05d3dea2009-06-22 11:32:31 -07001361 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001362
1363 # Create all the symlinks that don't already exist, or point to
1364 # somewhere different than what we want. Delete each symlink before
1365 # creating it, since the 'symlink' command won't overwrite.
1366 to_create = []
1367 for dest, link in target_symlinks:
1368 if link in source_symlinks_d:
1369 if dest != source_symlinks_d[link]:
1370 to_create.append((dest, link))
1371 else:
1372 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001373 script.DeleteFiles([i[1] for i in to_create])
1374 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001375
1376 # Now that the symlinks are created, we can set all the
1377 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001378 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001379
Doug Zongker881dd402009-09-20 14:03:55 -07001380 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001381 device_specific.IncrementalOTA_InstallEnd()
1382
Doug Zongker1c390a22009-05-14 19:06:36 -07001383 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001384 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001385
Doug Zongkere92f15a2011-08-26 13:46:40 -07001386 # Patch the build.prop file last, so if something fails but the
1387 # device can still come up, it appears to be the old build and will
1388 # get set the OTA package again to retry.
1389 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001390 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001391
Doug Zongker922206e2014-03-04 13:16:24 -08001392 if OPTIONS.wipe_user_data:
1393 script.Print("Erasing user data...")
1394 script.FormatPartition("/data")
1395
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001396 if OPTIONS.two_step:
1397 script.AppendExtra("""
1398set_stage("%(bcb_dev)s", "");
1399endif;
1400endif;
1401""" % bcb_dev)
1402
Doug Zongker25568482014-03-03 10:21:27 -08001403 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001404 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001405
1406
1407def main(argv):
1408
1409 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001410 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001411 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001412 elif o in ("-k", "--package_key"):
1413 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001414 elif o in ("-i", "--incremental_from"):
1415 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001416 elif o in ("-w", "--wipe_user_data"):
1417 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001418 elif o in ("-n", "--no_prereq"):
1419 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001420 elif o in ("-o", "--oem_settings"):
1421 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001422 elif o in ("-e", "--extra_script"):
1423 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001424 elif o in ("-a", "--aslr_mode"):
1425 if a in ("on", "On", "true", "True", "yes", "Yes"):
1426 OPTIONS.aslr_mode = True
1427 else:
1428 OPTIONS.aslr_mode = False
Martin Blumenstingl374e1142014-05-31 20:42:55 +02001429 elif o in ("-t", "--worker_threads"):
1430 if a.isdigit():
1431 OPTIONS.worker_threads = int(a)
1432 else:
1433 raise ValueError("Cannot parse value %r for option %r - only "
1434 "integers are allowed." % (a, o))
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001435 elif o in ("-2", "--two_step"):
1436 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001437 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001438 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001439 elif o == "--block":
1440 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001441 elif o in ("-b", "--binary"):
1442 OPTIONS.updater_binary = a
Doug Zongker62d4f182014-08-04 16:06:43 -07001443 elif o in ("--no_fallback_to_full",):
1444 OPTIONS.fallback_to_full = False
Doug Zongkereef39442009-04-02 12:14:19 -07001445 else:
1446 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001447 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001448
1449 args = common.ParseOptions(argv, __doc__,
Ying Wangf5770d72014-06-19 10:32:35 -07001450 extra_opts="b:k:i:d:wne:t:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001451 extra_long_opts=["board_config=",
1452 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001453 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001454 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001455 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001456 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001457 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001458 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001459 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001460 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001461 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001462 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001463 "oem_settings=",
Doug Zongker62d4f182014-08-04 16:06:43 -07001464 "no_fallback_to_full",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001465 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001466 extra_option_handler=option_handler)
1467
1468 if len(args) != 2:
1469 common.Usage(__doc__)
1470 sys.exit(1)
1471
Doug Zongker1c390a22009-05-14 19:06:36 -07001472 if OPTIONS.extra_script is not None:
1473 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1474
Doug Zongkereef39442009-04-02 12:14:19 -07001475 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001476 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001477
Doug Zongkereef39442009-04-02 12:14:19 -07001478 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001479 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001480
1481 # If this image was originally labelled with SELinux contexts, make sure we
1482 # also apply the labels in our new image. During building, the "file_contexts"
1483 # is in the out/ directory tree, but for repacking from target-files.zip it's
1484 # in the root directory of the ramdisk.
1485 if "selinux_fc" in OPTIONS.info_dict:
1486 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1487 "file_contexts")
1488
Doug Zongker37974732010-09-16 17:44:38 -07001489 if OPTIONS.verbose:
1490 print "--- target info ---"
1491 common.DumpInfoDict(OPTIONS.info_dict)
1492
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001493 # If the caller explicitly specified the device-specific extensions
1494 # path via -s/--device_specific, use that. Otherwise, use
1495 # META/releasetools.py if it is present in the target target_files.
1496 # Otherwise, take the path of the file from 'tool_extensions' in the
1497 # info dict and look for that in the local filesystem, relative to
1498 # the current directory.
1499
Doug Zongker37974732010-09-16 17:44:38 -07001500 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001501 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1502 if os.path.exists(from_input):
1503 print "(using device-specific extensions from target_files)"
1504 OPTIONS.device_specific = from_input
1505 else:
1506 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1507
Doug Zongker37974732010-09-16 17:44:38 -07001508 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001509 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001510
Doug Zongker62d4f182014-08-04 16:06:43 -07001511 while True:
Doug Zongkereef39442009-04-02 12:14:19 -07001512
Doug Zongker62d4f182014-08-04 16:06:43 -07001513 if OPTIONS.no_signing:
1514 if os.path.exists(args[1]): os.unlink(args[1])
1515 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1516 else:
1517 temp_zip_file = tempfile.NamedTemporaryFile()
1518 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1519 compression=zipfile.ZIP_DEFLATED)
1520
1521 if OPTIONS.incremental_source is None:
1522 WriteFullOTAPackage(input_zip, output_zip)
1523 if OPTIONS.package_key is None:
1524 OPTIONS.package_key = OPTIONS.info_dict.get(
1525 "default_system_dev_certificate",
1526 "build/target/product/security/testkey")
1527 break
1528
1529 else:
1530 print "unzipping source target-files..."
1531 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
1532 OPTIONS.target_info_dict = OPTIONS.info_dict
1533 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
1534 if "selinux_fc" in OPTIONS.source_info_dict:
1535 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1536 "file_contexts")
1537 if OPTIONS.package_key is None:
1538 OPTIONS.package_key = OPTIONS.source_info_dict.get(
1539 "default_system_dev_certificate",
1540 "build/target/product/security/testkey")
1541 if OPTIONS.verbose:
1542 print "--- source info ---"
1543 common.DumpInfoDict(OPTIONS.source_info_dict)
1544 try:
1545 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
1546 break
1547 except ValueError:
1548 if not OPTIONS.fallback_to_full: raise
1549 print "--- failed to build incremental; falling back to full ---"
1550 OPTIONS.incremental_source = None
1551 output_zip.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001552
1553 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001554
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001555 if not OPTIONS.no_signing:
1556 SignOutput(temp_zip_file.name, args[1])
1557 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001558
1559 common.Cleanup()
1560
1561 print "done."
1562
1563
1564if __name__ == '__main__':
1565 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001566 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001567 main(sys.argv[1:])
1568 except common.ExternalError, e:
1569 print
1570 print " ERROR: %s" % (e,)
1571 print
1572 sys.exit(1)