blob: 85888f83805aef726b4e41b935f18b849b1bdacf [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
Doug Zongkereef39442009-04-02 12:14:19 -070074"""
75
76import sys
77
Doug Zongkercf6d5a92014-02-18 10:57:07 -080078if sys.hexversion < 0x02070000:
79 print >> sys.stderr, "Python 2.7 or newer is required."
Doug Zongkereef39442009-04-02 12:14:19 -070080 sys.exit(1)
81
82import copy
Doug Zongkerc18736b2009-09-30 09:20:32 -070083import errno
Doug Zongkereef39442009-04-02 12:14:19 -070084import os
85import re
Doug Zongkereef39442009-04-02 12:14:19 -070086import subprocess
87import tempfile
88import time
89import zipfile
90
davidcad0bb92011-03-15 14:21:38 +000091try:
92 from hashlib import sha1 as sha1
93except ImportError:
94 from sha import sha as sha1
95
Doug Zongkereef39442009-04-02 12:14:19 -070096import common
Doug Zongker01ce19c2014-02-04 13:48:15 -080097import img_from_target_files
Doug Zongkerc494d7c2009-06-18 08:43:44 -070098import edify_generator
Geremy Condra36bd3652014-02-06 19:45:10 -080099import build_image
Doug Zongkereef39442009-04-02 12:14:19 -0700100
101OPTIONS = common.OPTIONS
Doug Zongkerafb32ea2011-09-22 10:28:04 -0700102OPTIONS.package_key = None
Doug Zongkereef39442009-04-02 12:14:19 -0700103OPTIONS.incremental_source = None
104OPTIONS.require_verbatim = set()
105OPTIONS.prohibit_verbatim = set(("system/build.prop",))
106OPTIONS.patch_threshold = 0.95
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700107OPTIONS.wipe_user_data = False
Doug Zongker962069c2009-04-23 11:41:58 -0700108OPTIONS.omit_prereq = False
Doug Zongker1c390a22009-05-14 19:06:36 -0700109OPTIONS.extra_script = None
Hristo Bojinovdafb0422010-08-26 14:35:16 -0700110OPTIONS.aslr_mode = True
Doug Zongker761e6422009-09-25 10:45:39 -0700111OPTIONS.worker_threads = 3
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800112OPTIONS.two_step = False
Takeshi Kanemotoe153b342013-11-14 17:20:50 +0900113OPTIONS.no_signing = False
Doug Zongker26e66192014-02-20 13:22:07 -0800114OPTIONS.block_based = False
Doug Zongker25568482014-03-03 10:21:27 -0800115OPTIONS.updater_binary = None
Michael Runge6e836112014-04-15 17:40:21 -0700116OPTIONS.oem_source = None
Doug Zongkereef39442009-04-02 12:14:19 -0700117
118def MostPopularKey(d, default):
119 """Given a dict, return the key corresponding to the largest
120 value. Returns 'default' if the dict is empty."""
121 x = [(v, k) for (k, v) in d.iteritems()]
122 if not x: return default
123 x.sort()
124 return x[-1][1]
125
126
127def IsSymlink(info):
128 """Return true if the zipfile.ZipInfo object passed in represents a
129 symlink."""
130 return (info.external_attr >> 16) == 0120777
131
Hristo Bojinov96be7202010-08-02 10:26:17 -0700132def IsRegular(info):
133 """Return true if the zipfile.ZipInfo object passed in represents a
134 symlink."""
135 return (info.external_attr >> 28) == 010
Doug Zongkereef39442009-04-02 12:14:19 -0700136
Michael Runge4038aa82013-12-13 18:06:28 -0800137def ClosestFileMatch(src, tgtfiles, existing):
138 """Returns the closest file match between a source file and list
139 of potential matches. The exact filename match is preferred,
140 then the sha1 is searched for, and finally a file with the same
141 basename is evaluated. Rename support in the updater-binary is
142 required for the latter checks to be used."""
143
144 result = tgtfiles.get("path:" + src.name)
145 if result is not None:
146 return result
147
148 if not OPTIONS.target_info_dict.get("update_rename_support", False):
149 return None
150
151 if src.size < 1000:
152 return None
153
154 result = tgtfiles.get("sha1:" + src.sha1)
155 if result is not None and existing.get(result.name) is None:
156 return result
157 result = tgtfiles.get("file:" + src.name.split("/")[-1])
158 if result is not None and existing.get(result.name) is None:
159 return result
160 return None
161
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700162class ItemSet:
163 def __init__(self, partition, fs_config):
164 self.partition = partition
165 self.fs_config = fs_config
166 self.ITEMS = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700167
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700168 def Get(self, name, dir=False):
169 if name not in self.ITEMS:
170 self.ITEMS[name] = Item(self, name, dir=dir)
171 return self.ITEMS[name]
Doug Zongkereef39442009-04-02 12:14:19 -0700172
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700173 def GetMetadata(self, input_zip):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700174 # The target_files contains a record of what the uid,
175 # gid, and mode are supposed to be.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700176 output = input_zip.read(self.fs_config)
Doug Zongkereef39442009-04-02 12:14:19 -0700177
178 for line in output.split("\n"):
179 if not line: continue
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700180 columns = line.split()
181 name, uid, gid, mode = columns[:4]
182 selabel = None
183 capabilities = None
184
185 # After the first 4 columns, there are a series of key=value
186 # pairs. Extract out the fields we care about.
187 for element in columns[4:]:
188 key, value = element.split("=")
189 if key == "selabel":
190 selabel = value
191 if key == "capabilities":
192 capabilities = value
193
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700194 i = self.ITEMS.get(name, None)
Doug Zongker283e2a12010-03-15 17:52:32 -0700195 if i is not None:
196 i.uid = int(uid)
197 i.gid = int(gid)
198 i.mode = int(mode, 8)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700199 i.selabel = selabel
200 i.capabilities = capabilities
Doug Zongker283e2a12010-03-15 17:52:32 -0700201 if i.dir:
202 i.children.sort(key=lambda i: i.name)
203
204 # set metadata for the files generated by this script.
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700205 i = self.ITEMS.get("system/recovery-from-boot.p", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700206 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0644, None, None
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700207 i = self.ITEMS.get("system/etc/install-recovery.sh", None)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700208 if i: i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0544, None, None
Doug Zongkereef39442009-04-02 12:14:19 -0700209
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700210
211class Item:
212 """Items represent the metadata (user, group, mode) of files and
213 directories in the system image."""
214 def __init__(self, itemset, name, dir=False):
215 self.itemset = itemset
216 self.name = name
217 self.uid = None
218 self.gid = None
219 self.mode = None
220 self.selabel = None
221 self.capabilities = None
222 self.dir = dir
223
224 if name:
225 self.parent = itemset.Get(os.path.dirname(name), dir=True)
226 self.parent.children.append(self)
227 else:
228 self.parent = None
229 if dir:
230 self.children = []
231
232 def Dump(self, indent=0):
233 if self.uid is not None:
234 print "%s%s %d %d %o" % (" "*indent, self.name, self.uid, self.gid, self.mode)
235 else:
236 print "%s%s %s %s %s" % (" "*indent, self.name, self.uid, self.gid, self.mode)
237 if self.dir:
238 print "%s%s" % (" "*indent, self.descendants)
239 print "%s%s" % (" "*indent, self.best_subtree)
240 for i in self.children:
241 i.Dump(indent=indent+1)
242
Doug Zongkereef39442009-04-02 12:14:19 -0700243 def CountChildMetadata(self):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700244 """Count up the (uid, gid, mode, selabel, capabilities) tuples for
245 all children and determine the best strategy for using set_perm_recursive and
Doug Zongkereef39442009-04-02 12:14:19 -0700246 set_perm to correctly chown/chmod all the files to their desired
247 values. Recursively calls itself for all descendants.
248
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700249 Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count} counting up
Doug Zongkereef39442009-04-02 12:14:19 -0700250 all descendants of this node. (dmode or fmode may be None.) Also
251 sets the best_subtree of each directory Item to the (uid, gid,
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700252 dmode, fmode, selabel, capabilities) tuple that will match the most
253 descendants of that Item.
Doug Zongkereef39442009-04-02 12:14:19 -0700254 """
255
256 assert self.dir
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700257 d = self.descendants = {(self.uid, self.gid, self.mode, None, self.selabel, self.capabilities): 1}
Doug Zongkereef39442009-04-02 12:14:19 -0700258 for i in self.children:
259 if i.dir:
260 for k, v in i.CountChildMetadata().iteritems():
261 d[k] = d.get(k, 0) + v
262 else:
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700263 k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700264 d[k] = d.get(k, 0) + 1
265
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700266 # Find the (uid, gid, dmode, fmode, selabel, capabilities)
267 # tuple that matches the most descendants.
Doug Zongkereef39442009-04-02 12:14:19 -0700268
269 # First, find the (uid, gid) pair that matches the most
270 # descendants.
271 ug = {}
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700272 for (uid, gid, _, _, _, _), count in d.iteritems():
Doug Zongkereef39442009-04-02 12:14:19 -0700273 ug[(uid, gid)] = ug.get((uid, gid), 0) + count
274 ug = MostPopularKey(ug, (0, 0))
275
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700276 # Now find the dmode, fmode, selabel, and capabilities that match
277 # the most descendants with that (uid, gid), and choose those.
Doug Zongkereef39442009-04-02 12:14:19 -0700278 best_dmode = (0, 0755)
279 best_fmode = (0, 0644)
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700280 best_selabel = (0, None)
281 best_capabilities = (0, None)
Doug Zongkereef39442009-04-02 12:14:19 -0700282 for k, count in d.iteritems():
283 if k[:2] != ug: continue
284 if k[2] is not None and count >= best_dmode[0]: best_dmode = (count, k[2])
285 if k[3] is not None and count >= best_fmode[0]: best_fmode = (count, k[3])
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700286 if k[4] is not None and count >= best_selabel[0]: best_selabel = (count, k[4])
287 if k[5] is not None and count >= best_capabilities[0]: best_capabilities = (count, k[5])
288 self.best_subtree = ug + (best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
Doug Zongkereef39442009-04-02 12:14:19 -0700289
290 return d
291
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700292 def SetPermissions(self, script):
Doug Zongkereef39442009-04-02 12:14:19 -0700293 """Append set_perm/set_perm_recursive commands to 'script' to
294 set all permissions, users, and groups for the tree of files
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700295 rooted at 'self'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700296
297 self.CountChildMetadata()
298
299 def recurse(item, current):
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700300 # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple that the current
Doug Zongkereef39442009-04-02 12:14:19 -0700301 # item (and all its children) have already been set to. We only
302 # need to issue set_perm/set_perm_recursive commands if we're
303 # supposed to be something different.
304 if item.dir:
305 if current != item.best_subtree:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700306 script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
Doug Zongkereef39442009-04-02 12:14:19 -0700307 current = item.best_subtree
308
309 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700310 item.mode != current[2] or item.selabel != current[4] or \
311 item.capabilities != current[5]:
312 script.SetPermissions("/"+item.name, item.uid, item.gid,
313 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700314
315 for i in item.children:
316 recurse(i, current)
317 else:
318 if item.uid != current[0] or item.gid != current[1] or \
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700319 item.mode != current[3] or item.selabel != current[4] or \
320 item.capabilities != current[5]:
321 script.SetPermissions("/"+item.name, item.uid, item.gid,
322 item.mode, item.selabel, item.capabilities)
Doug Zongkereef39442009-04-02 12:14:19 -0700323
Nick Kralevich0eb17d92013-09-07 17:10:29 -0700324 recurse(self, (-1, -1, -1, -1, None, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700325
326
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700327def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
328 """Copies files for the partition in the input zip to the output
Doug Zongkereef39442009-04-02 12:14:19 -0700329 zip. Populates the Item class with their metadata, and returns a
Doug Zongker1807e702012-02-28 12:21:08 -0800330 list of symlinks. output_zip may be None, in which case the copy is
331 skipped (but the other side effects still happen). substitute is an
332 optional dict of {output filename: contents} to be output instead of
333 certain input files.
Doug Zongkereef39442009-04-02 12:14:19 -0700334 """
335
336 symlinks = []
337
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700338 partition = itemset.partition
339
Doug Zongkereef39442009-04-02 12:14:19 -0700340 for info in input_zip.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700341 if info.filename.startswith(partition.upper() + "/"):
Doug Zongkereef39442009-04-02 12:14:19 -0700342 basefilename = info.filename[7:]
343 if IsSymlink(info):
344 symlinks.append((input_zip.read(info.filename),
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700345 "/" + partition + "/" + basefilename))
Doug Zongkereef39442009-04-02 12:14:19 -0700346 else:
347 info2 = copy.copy(info)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700348 fn = info2.filename = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700349 if substitute and fn in substitute and substitute[fn] is None:
350 continue
351 if output_zip is not None:
352 if substitute and fn in substitute:
353 data = substitute[fn]
354 else:
355 data = input_zip.read(info.filename)
356 output_zip.writestr(info2, data)
357 if fn.endswith("/"):
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700358 itemset.Get(fn[:-1], dir=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700359 else:
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700360 itemset.Get(fn, dir=False)
Doug Zongkereef39442009-04-02 12:14:19 -0700361
362 symlinks.sort()
Doug Zongker1807e702012-02-28 12:21:08 -0800363 return symlinks
Doug Zongkereef39442009-04-02 12:14:19 -0700364
365
Doug Zongkereef39442009-04-02 12:14:19 -0700366def SignOutput(temp_zip_name, output_zip_name):
367 key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
368 pw = key_passwords[OPTIONS.package_key]
369
Doug Zongker951495f2009-08-14 12:44:19 -0700370 common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
371 whole_file=True)
Doug Zongkereef39442009-04-02 12:14:19 -0700372
373
Michael Rungec6e3afd2014-05-05 11:55:47 -0700374def AppendAssertions(script, info_dict, oem_dict = None):
Michael Runge6e836112014-04-15 17:40:21 -0700375 oem_props = info_dict.get("oem_fingerprint_properties")
376 if oem_props is None:
377 device = GetBuildProp("ro.product.device", info_dict)
378 script.AssertDevice(device)
379 else:
380 if oem_dict is None:
381 raise common.ExternalError("No OEM file provided to answer expected assertions")
382 for prop in oem_props.split():
383 if oem_dict.get(prop) is None:
384 raise common.ExternalError("The OEM file is missing the property %s" % prop)
385 script.AssertOemProperty(prop, oem_dict.get(prop))
Doug Zongkereef39442009-04-02 12:14:19 -0700386
Doug Zongkereef39442009-04-02 12:14:19 -0700387
Doug Zongkerc9253822014-02-04 12:17:58 -0800388def HasRecoveryPatch(target_files_zip):
389 try:
390 target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
391 return True
392 except KeyError:
393 return False
Doug Zongker73ef8252009-07-23 15:12:53 -0700394
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700395def HasVendorPartition(target_files_zip):
396 try:
397 target_files_zip.getinfo("VENDOR/")
398 return True
399 except KeyError:
400 return False
401
Michael Runge6e836112014-04-15 17:40:21 -0700402def GetOemProperty(name, oem_props, oem_dict, info_dict):
403 if oem_props is not None and name in oem_props:
404 return oem_dict[name]
405 return GetBuildProp(name, info_dict)
406
407
408def CalculateFingerprint(oem_props, oem_dict, info_dict):
409 if oem_props is None:
410 return GetBuildProp("ro.build.fingerprint", info_dict)
411 return "%s/%s/%s:%s" % (
412 GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
413 GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
414 GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
415 GetBuildProp("ro.build.thumbprint", info_dict))
Doug Zongker73ef8252009-07-23 15:12:53 -0700416
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700417def WriteFullOTAPackage(input_zip, output_zip):
Doug Zongker9ce2ebf2010-04-21 14:08:44 -0700418 # TODO: how to determine this? We don't know what version it will
419 # be installed on top of. For now, we expect the API just won't
420 # change very often.
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700421 script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -0700422
Michael Runge6e836112014-04-15 17:40:21 -0700423 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
424 oem_dict = None
425 if oem_props is not None:
426 if OPTIONS.oem_source is None:
427 raise common.ExternalError("OEM source required for this build")
428 script.Mount("/oem")
429 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
430
431 metadata = {"post-build": CalculateFingerprint(
432 oem_props, oem_dict, OPTIONS.info_dict),
433 "pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700434 OPTIONS.info_dict),
435 "post-timestamp": GetBuildProp("ro.build.date.utc",
436 OPTIONS.info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -0700437 }
438
Doug Zongker05d3dea2009-06-22 11:32:31 -0700439 device_specific = common.DeviceSpecificParams(
440 input_zip=input_zip,
Doug Zongker37974732010-09-16 17:44:38 -0700441 input_version=OPTIONS.info_dict["recovery_api_version"],
Doug Zongker05d3dea2009-06-22 11:32:31 -0700442 output_zip=output_zip,
443 script=script,
Doug Zongker2ea21062010-04-28 16:05:21 -0700444 input_tmp=OPTIONS.input_tmp,
Doug Zongker96a57e72010-09-26 14:57:41 -0700445 metadata=metadata,
446 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700447
Doug Zongkerc9253822014-02-04 12:17:58 -0800448 has_recovery_patch = HasRecoveryPatch(input_zip)
Doug Zongker26e66192014-02-20 13:22:07 -0800449 block_based = OPTIONS.block_based and has_recovery_patch
Doug Zongkerc9253822014-02-04 12:17:58 -0800450
Doug Zongker962069c2009-04-23 11:41:58 -0700451 if not OPTIONS.omit_prereq:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700452 ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
Doug Zongker0d92f1f2013-06-03 12:07:12 -0700453 ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
454 script.AssertOlderBuild(ts, ts_text)
Doug Zongkereef39442009-04-02 12:14:19 -0700455
Michael Runge6e836112014-04-15 17:40:21 -0700456 AppendAssertions(script, OPTIONS.info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700457 device_specific.FullOTA_Assertions()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800458
459 # Two-step package strategy (in chronological order, which is *not*
460 # the order in which the generated script has things):
461 #
462 # if stage is not "2/3" or "3/3":
463 # write recovery image to boot partition
464 # set stage to "2/3"
465 # reboot to boot partition and restart recovery
466 # else if stage is "2/3":
467 # write recovery image to recovery partition
468 # set stage to "3/3"
469 # reboot to recovery partition and restart recovery
470 # else:
471 # (stage must be "3/3")
472 # set stage to ""
473 # do normal full package installation:
474 # wipe and install system, boot image, etc.
475 # set up system to update recovery partition on first boot
476 # complete script normally (allow recovery to mark itself finished and reboot)
477
478 recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
479 OPTIONS.input_tmp, "RECOVERY")
480 if OPTIONS.two_step:
481 if not OPTIONS.info_dict.get("multistage_support", None):
482 assert False, "two-step packages not supported by this build"
483 fs = OPTIONS.info_dict["fstab"]["/misc"]
484 assert fs.fs_type.upper() == "EMMC", \
485 "two-step packages only supported on devices with EMMC /misc partitions"
486 bcb_dev = {"bcb_dev": fs.device}
487 common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
488 script.AppendExtra("""
489if get_stage("%(bcb_dev)s", "stage") == "2/3" then
490""" % bcb_dev)
491 script.WriteRawImage("/recovery", "recovery.img")
492 script.AppendExtra("""
493set_stage("%(bcb_dev)s", "3/3");
494reboot_now("%(bcb_dev)s", "recovery");
495else if get_stage("%(bcb_dev)s", "stage") == "3/3" then
496""" % bcb_dev)
497
Doug Zongkere5ff5902012-01-17 10:55:37 -0800498 device_specific.FullOTA_InstallBegin()
Doug Zongker171f1cd2009-06-15 22:36:37 -0700499
Doug Zongker01ce19c2014-02-04 13:48:15 -0800500 system_progress = 0.75
Doug Zongkereef39442009-04-02 12:14:19 -0700501
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700502 if OPTIONS.wipe_user_data:
Doug Zongker01ce19c2014-02-04 13:48:15 -0800503 system_progress -= 0.1
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700504 if HasVendorPartition(input_zip):
505 system_progress -= 0.1
Doug Zongkerdbfaae52009-04-21 17:12:54 -0700506
Kenny Rootf32dc712012-04-08 10:42:34 -0700507 if "selinux_fc" in OPTIONS.info_dict:
508 WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
Stephen Smalley56882bf2012-02-09 13:36:21 -0500509
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700510 system_items = ItemSet("system", "META/filesystem_config.txt")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700511 script.ShowProgress(system_progress, 0)
Doug Zongker26e66192014-02-20 13:22:07 -0800512 if block_based:
Doug Zongker5fad2032014-02-24 08:13:45 -0800513 mapdata, data = img_from_target_files.BuildSystem(
514 OPTIONS.input_tmp, OPTIONS.info_dict,
515 sparse=False, map_file=True)
516
517 common.ZipWriteStr(output_zip, "system.map", mapdata)
518 common.ZipWriteStr(output_zip, "system.muimg", data)
519 script.WipeBlockDevice("/system")
520 script.WriteRawImage("/system", "system.muimg", mapfn="system.map")
Doug Zongker01ce19c2014-02-04 13:48:15 -0800521 else:
522 script.FormatPartition("/system")
523 script.Mount("/system")
524 if not has_recovery_patch:
525 script.UnpackPackageDir("recovery", "/system")
Doug Zongker26e66192014-02-20 13:22:07 -0800526 script.UnpackPackageDir("system", "/system")
Doug Zongkereef39442009-04-02 12:14:19 -0700527
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700528 symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
Doug Zongker01ce19c2014-02-04 13:48:15 -0800529 script.MakeSymlinks(symlinks)
Doug Zongkereef39442009-04-02 12:14:19 -0700530
Doug Zongker55d93282011-01-25 17:03:34 -0800531 boot_img = common.GetBootableImage("boot.img", "boot.img",
532 OPTIONS.input_tmp, "BOOT")
Doug Zongkerc9253822014-02-04 12:17:58 -0800533
Doug Zongker91a99c22014-05-09 13:15:01 -0700534 if not block_based:
Doug Zongkerc9253822014-02-04 12:17:58 -0800535 def output_sink(fn, data):
536 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700537 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -0800538
539 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
540 recovery_img, boot_img)
Doug Zongkereef39442009-04-02 12:14:19 -0700541
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700542 system_items.GetMetadata(input_zip)
543 system_items.Get("system").SetPermissions(script)
544
545 if HasVendorPartition(input_zip):
546 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
547 script.ShowProgress(0.1, 0)
548
549 if block_based:
550 mapdata, data = img_from_target_files.BuildVendor(
551 OPTIONS.input_tmp, OPTIONS.info_dict,
552 sparse=False, map_file=True)
553
554 common.ZipWriteStr(output_zip, "vendor.map", mapdata)
555 common.ZipWriteStr(output_zip, "vendor.muimg", data)
556 script.WipeBlockDevice("/vendor")
557 script.WriteRawImage("/vendor", "vendor.muimg", mapfn="vendor.map")
558 else:
559 script.FormatPartition("/vendor")
560 script.Mount("/vendor")
561 script.UnpackPackageDir("vendor", "/vendor")
562
563 symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
564 script.MakeSymlinks(symlinks)
565
566 vendor_items.GetMetadata(input_zip)
567 vendor_items.Get("vendor").SetPermissions(script)
Doug Zongker73ef8252009-07-23 15:12:53 -0700568
Doug Zongker37974732010-09-16 17:44:38 -0700569 common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
Doug Zongker73ef8252009-07-23 15:12:53 -0700570 common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700571
Doug Zongker01ce19c2014-02-04 13:48:15 -0800572 script.ShowProgress(0.05, 5)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700573 script.WriteRawImage("/boot", "boot.img")
Doug Zongker05d3dea2009-06-22 11:32:31 -0700574
Doug Zongker01ce19c2014-02-04 13:48:15 -0800575 script.ShowProgress(0.2, 10)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700576 device_specific.FullOTA_InstallEnd()
Doug Zongkereef39442009-04-02 12:14:19 -0700577
Doug Zongker1c390a22009-05-14 19:06:36 -0700578 if OPTIONS.extra_script is not None:
Doug Zongkerc494d7c2009-06-18 08:43:44 -0700579 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -0700580
Doug Zongker14833602010-02-02 13:12:04 -0800581 script.UnmountAll()
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800582
Doug Zongker922206e2014-03-04 13:16:24 -0800583 if OPTIONS.wipe_user_data:
584 script.ShowProgress(0.1, 10)
585 script.FormatPartition("/data")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700586
Doug Zongker9b23f2c2013-11-25 14:44:12 -0800587 if OPTIONS.two_step:
588 script.AppendExtra("""
589set_stage("%(bcb_dev)s", "");
590""" % bcb_dev)
591 script.AppendExtra("else\n")
592 script.WriteRawImage("/boot", "recovery.img")
593 script.AppendExtra("""
594set_stage("%(bcb_dev)s", "2/3");
595reboot_now("%(bcb_dev)s", "");
596endif;
597endif;
598""" % bcb_dev)
Doug Zongker25568482014-03-03 10:21:27 -0800599 script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -0700600 WriteMetadata(metadata, output_zip)
601
Stephen Smalley56882bf2012-02-09 13:36:21 -0500602def WritePolicyConfig(file_context, output_zip):
603 f = open(file_context, 'r');
604 basename = os.path.basename(file_context)
605 common.ZipWriteStr(output_zip, basename, f.read())
606
Doug Zongker2ea21062010-04-28 16:05:21 -0700607
608def WriteMetadata(metadata, output_zip):
609 common.ZipWriteStr(output_zip, "META-INF/com/android/metadata",
610 "".join(["%s=%s\n" % kv
611 for kv in sorted(metadata.iteritems())]))
Doug Zongkereef39442009-04-02 12:14:19 -0700612
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700613def LoadPartitionFiles(z, partition):
614 """Load all the files from the given partition in a given target-files
Doug Zongkereef39442009-04-02 12:14:19 -0700615 ZipFile, and return a dict of {filename: File object}."""
616 out = {}
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700617 prefix = partition.upper() + "/"
Doug Zongkereef39442009-04-02 12:14:19 -0700618 for info in z.infolist():
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700619 if info.filename.startswith(prefix) and not IsSymlink(info):
Hristo Bojinov96be7202010-08-02 10:26:17 -0700620 basefilename = info.filename[7:]
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700621 fn = partition + "/" + basefilename
Doug Zongkereef39442009-04-02 12:14:19 -0700622 data = z.read(info.filename)
Doug Zongkerea5d7a92010-09-12 15:26:16 -0700623 out[fn] = common.File(fn, data)
Doug Zongker1807e702012-02-28 12:21:08 -0800624 return out
Doug Zongkereef39442009-04-02 12:14:19 -0700625
626
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700627def GetBuildProp(prop, info_dict):
628 """Return the fingerprint of the build of a given target-files info_dict."""
629 try:
630 return info_dict.get("build.prop", {})[prop]
631 except KeyError:
Ying Wangc73e4612014-04-15 15:27:43 -0700632 raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
Doug Zongkereef39442009-04-02 12:14:19 -0700633
Michael Runge4038aa82013-12-13 18:06:28 -0800634def AddToKnownPaths(filename, known_paths):
635 if filename[-1] == "/":
636 return
637 dirs = filename.split("/")[:-1]
638 while len(dirs) > 0:
639 path = "/".join(dirs)
640 if path in known_paths:
641 break;
642 known_paths.add(path)
643 dirs.pop()
Doug Zongkereef39442009-04-02 12:14:19 -0700644
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700645class BlockDifference:
646 def __init__(self, partition, builder, output_zip):
647 with tempfile.NamedTemporaryFile() as src_file:
648 with tempfile.NamedTemporaryFile() as tgt_file:
649 print "building source " + partition + " image..."
650 src_file = tempfile.NamedTemporaryFile()
651 src_mapdata, src_data = builder(OPTIONS.source_tmp,
652 OPTIONS.source_info_dict,
653 sparse=False, map_file=True)
654
655 self.src_sha1 = sha1(src_data).hexdigest()
656 print "source " + partition + " sha1:", self.src_sha1
657 src_file.write(src_data)
658
659 print "building target " + partition + " image..."
660 tgt_file = tempfile.NamedTemporaryFile()
661 tgt_mapdata, tgt_data = builder(OPTIONS.target_tmp,
662 OPTIONS.target_info_dict,
663 sparse=False, map_file=True)
664 self.tgt_sha1 = sha1(tgt_data).hexdigest()
665 print "target " + partition + " sha1:", self.tgt_sha1
666 tgt_len = len(tgt_data)
667 tgt_file.write(tgt_data)
668
669 system_type, self.device = common.GetTypeAndDevice("/" + partition,
670 OPTIONS.info_dict)
671 self.patch = common.MakePartitionPatch(src_file, tgt_file, partition)
672
673 TestBlockPatch(src_data, src_mapdata, self.patch.data,
674 tgt_mapdata, self.tgt_sha1)
675 src_data = None
676 tgt_data = None
677
678 self.patch.AddToZip(output_zip, compression=zipfile.ZIP_STORED)
679 self.src_mapfilename = self.patch.name + ".src.map"
680 common.ZipWriteStr(output_zip, self.src_mapfilename, src_mapdata)
681 self.tgt_mapfilename = self.patch.name + ".tgt.map"
682 common.ZipWriteStr(output_zip, self.tgt_mapfilename, tgt_mapdata)
683
Geremy Condra36bd3652014-02-06 19:45:10 -0800684def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
685 source_version = OPTIONS.source_info_dict["recovery_api_version"]
686 target_version = OPTIONS.target_info_dict["recovery_api_version"]
687
688 if source_version == 0:
689 print ("WARNING: generating edify script for a source that "
690 "can't install it.")
691 script = edify_generator.EdifyGenerator(source_version,
692 OPTIONS.target_info_dict)
693
694 metadata = {"pre-device": GetBuildProp("ro.product.device",
695 OPTIONS.source_info_dict),
696 "post-timestamp": GetBuildProp("ro.build.date.utc",
697 OPTIONS.target_info_dict),
698 }
699
700 device_specific = common.DeviceSpecificParams(
701 source_zip=source_zip,
702 source_version=source_version,
703 target_zip=target_zip,
704 target_version=target_version,
705 output_zip=output_zip,
706 script=script,
707 metadata=metadata,
708 info_dict=OPTIONS.info_dict)
709
710 source_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.source_info_dict)
711 target_fp = GetBuildProp("ro.build.fingerprint", OPTIONS.target_info_dict)
712 metadata["pre-build"] = source_fp
713 metadata["post-build"] = target_fp
714
715 source_boot = common.GetBootableImage(
716 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
717 OPTIONS.source_info_dict)
718 target_boot = common.GetBootableImage(
719 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
720 updating_boot = (not OPTIONS.two_step and
721 (source_boot.data != target_boot.data))
722
723 source_recovery = common.GetBootableImage(
724 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
725 OPTIONS.source_info_dict)
726 target_recovery = common.GetBootableImage(
727 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
728 updating_recovery = (source_recovery.data != target_recovery.data)
729
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700730 system_diff = BlockDifference("system", img_from_target_files.BuildSystem,
731 output_zip)
732 if HasVendorPartition(target_zip):
733 if not HasVendorPartition(source_zip):
734 raise RuntimeError("can't generate incremental that adds /vendor")
735 vendor_diff = BlockDifference("vendor", img_from_target_files.BuildVendor,
736 output_zip)
Geremy Condra36bd3652014-02-06 19:45:10 -0800737
Michael Rungec6e3afd2014-05-05 11:55:47 -0700738 oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
739 oem_dict = None
740 if oem_props is not None:
741 if OPTIONS.oem_source is None:
742 raise common.ExternalError("OEM source required for this build")
743 script.Mount("/oem")
744 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
745
746 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Geremy Condra36bd3652014-02-06 19:45:10 -0800747 device_specific.IncrementalOTA_Assertions()
748
749 # Two-step incremental package strategy (in chronological order,
750 # which is *not* the order in which the generated script has
751 # things):
752 #
753 # if stage is not "2/3" or "3/3":
754 # do verification on current system
755 # write recovery image to boot partition
756 # set stage to "2/3"
757 # reboot to boot partition and restart recovery
758 # else if stage is "2/3":
759 # write recovery image to recovery partition
760 # set stage to "3/3"
761 # reboot to recovery partition and restart recovery
762 # else:
763 # (stage must be "3/3")
764 # perform update:
765 # patch system files, etc.
766 # force full install of new boot image
767 # set up system to update recovery partition on first boot
768 # complete script normally (allow recovery to mark itself finished and reboot)
769
770 if OPTIONS.two_step:
771 if not OPTIONS.info_dict.get("multistage_support", None):
772 assert False, "two-step packages not supported by this build"
773 fs = OPTIONS.info_dict["fstab"]["/misc"]
774 assert fs.fs_type.upper() == "EMMC", \
775 "two-step packages only supported on devices with EMMC /misc partitions"
776 bcb_dev = {"bcb_dev": fs.device}
777 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
778 script.AppendExtra("""
779if get_stage("%(bcb_dev)s", "stage") == "2/3" then
780""" % bcb_dev)
781 script.AppendExtra("sleep(20);\n");
782 script.WriteRawImage("/recovery", "recovery.img")
783 script.AppendExtra("""
784set_stage("%(bcb_dev)s", "3/3");
785reboot_now("%(bcb_dev)s", "recovery");
786else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
787""" % bcb_dev)
788
789 script.Print("Verifying current system...")
790
791 device_specific.IncrementalOTA_VerifyBegin()
792
Michael Rungec6e3afd2014-05-05 11:55:47 -0700793 if oem_props is None:
794 script.AssertSomeFingerprint(source_fp, target_fp)
795 else:
796 script.AssertSomeThumbprint(
797 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
798 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
Geremy Condra36bd3652014-02-06 19:45:10 -0800799
800 if updating_boot:
Geremy Condra36bd3652014-02-06 19:45:10 -0800801 d = common.Difference(target_boot, source_boot)
802 _, _, d = d.ComputePatch()
803 print "boot target: %d source: %d diff: %d" % (
804 target_boot.size, source_boot.size, len(d))
805
806 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
807
808 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
809
810 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
811 (boot_type, boot_device,
812 source_boot.size, source_boot.sha1,
813 target_boot.size, target_boot.sha1))
814
815 device_specific.IncrementalOTA_VerifyEnd()
816
817 if OPTIONS.two_step:
818 script.WriteRawImage("/boot", "recovery.img")
819 script.AppendExtra("""
820set_stage("%(bcb_dev)s", "2/3");
821reboot_now("%(bcb_dev)s", "");
822else
823""" % bcb_dev)
824
825 script.Comment("---- start making changes here ----")
826
827 device_specific.IncrementalOTA_InstallBegin()
828
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700829 if HasVendorPartition(target_zip):
830 script.Print("Patching vendor image...")
831 script.ShowProgress(0.1, 0)
832 script.Syspatch(vendor_diff.device,
833 vendor_diff.tgt_mapfilename, vendor_diff.tgt_sha1,
834 vendor_diff.src_mapfilename, vendor_diff.src_sha1,
835 vendor_diff.patch.name)
836 sys_progress = 0.8
837 else:
838 sys_progress = 0.9
839
Geremy Condra36bd3652014-02-06 19:45:10 -0800840 script.Print("Patching system image...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700841 script.ShowProgress(sys_progress, 0)
842 script.Syspatch(system_diff.device,
843 system_diff.tgt_mapfilename, system_diff.tgt_sha1,
844 system_diff.src_mapfilename, system_diff.src_sha1,
845 system_diff.patch.name)
Geremy Condra36bd3652014-02-06 19:45:10 -0800846
847 if OPTIONS.two_step:
848 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
849 script.WriteRawImage("/boot", "boot.img")
850 print "writing full boot image (forced by two-step mode)"
851
852 if not OPTIONS.two_step:
853 if updating_boot:
854 # Produce the boot image by applying a patch to the current
855 # contents of the boot partition, and write it back to the
856 # partition.
857 script.Print("Patching boot image...")
Doug Zongker4b9596f2014-06-09 14:15:45 -0700858 script.ShowProgress(0.1, 10)
Geremy Condra36bd3652014-02-06 19:45:10 -0800859 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
860 % (boot_type, boot_device,
861 source_boot.size, source_boot.sha1,
862 target_boot.size, target_boot.sha1),
863 "-",
864 target_boot.size, target_boot.sha1,
865 source_boot.sha1, "patch/boot.img.p")
866 print "boot image changed; including."
867 else:
868 print "boot image unchanged; skipping."
869
870 # Do device-specific installation (eg, write radio image).
871 device_specific.IncrementalOTA_InstallEnd()
872
873 if OPTIONS.extra_script is not None:
874 script.AppendExtra(OPTIONS.extra_script)
875
Doug Zongker922206e2014-03-04 13:16:24 -0800876 if OPTIONS.wipe_user_data:
877 script.Print("Erasing user data...")
878 script.FormatPartition("/data")
879
Geremy Condra36bd3652014-02-06 19:45:10 -0800880 if OPTIONS.two_step:
881 script.AppendExtra("""
882set_stage("%(bcb_dev)s", "");
883endif;
884endif;
885""" % bcb_dev)
886
887 script.SetProgress(1)
Doug Zongker25568482014-03-03 10:21:27 -0800888 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Geremy Condra36bd3652014-02-06 19:45:10 -0800889 WriteMetadata(metadata, output_zip)
890
Doug Zongker32b527d2014-03-04 10:03:02 -0800891def ParseMap(map_str):
892 x = map_str.split()
893 assert int(x[0]) == 4096
894 assert int(x[1]) == len(x)-2
895 return int(x[0]), [int(i) for i in x[2:]]
896
897def TestBlockPatch(src_muimg, src_map, patch_data, tgt_map, tgt_sha1):
898 src_blksize, src_regions = ParseMap(src_map)
899 tgt_blksize, tgt_regions = ParseMap(tgt_map)
900
901 with tempfile.NamedTemporaryFile() as src_file,\
902 tempfile.NamedTemporaryFile() as patch_file,\
Doug Zongker32b527d2014-03-04 10:03:02 -0800903 tempfile.NamedTemporaryFile() as src_map_file,\
904 tempfile.NamedTemporaryFile() as tgt_map_file:
905
906 src_total = sum(src_regions) * src_blksize
907 src_file.truncate(src_total)
908 p = 0
909 for i in range(0, len(src_regions), 2):
910 c, dc = src_regions[i:i+2]
911 src_file.write(src_muimg[p:(p+c*src_blksize)])
912 p += c*src_blksize
913 src_file.seek(dc*src_blksize, 1)
914 assert src_file.tell() == src_total
915
916 patch_file.write(patch_data)
917
Doug Zongker32b527d2014-03-04 10:03:02 -0800918 src_map_file.write(src_map)
919 tgt_map_file.write(tgt_map)
920
921 src_file.flush()
922 src_map_file.flush()
923 patch_file.flush()
Doug Zongker32b527d2014-03-04 10:03:02 -0800924 tgt_map_file.flush()
925
926 p = common.Run(["syspatch_host", src_file.name, src_map_file.name,
Doug Zongker1113e382014-06-13 10:38:32 -0700927 patch_file.name, src_file.name, tgt_map_file.name],
Doug Zongker32b527d2014-03-04 10:03:02 -0800928 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
929 stdoutdata, _ = p.communicate()
930 if p.returncode != 0:
931 print stdoutdata
932 raise ValueError("failed to reconstruct target system image from patch")
933
934 h = sha1()
Doug Zongker1113e382014-06-13 10:38:32 -0700935 src_file.seek(0, 0)
Doug Zongker32b527d2014-03-04 10:03:02 -0800936 for i in range(0, len(tgt_regions), 2):
937 c, dc = tgt_regions[i:i+2]
Doug Zongker1113e382014-06-13 10:38:32 -0700938 h.update(src_file.read(c*tgt_blksize))
939 src_file.seek(dc*tgt_blksize, 1)
Doug Zongker32b527d2014-03-04 10:03:02 -0800940
941 if h.hexdigest() != tgt_sha1:
942 raise ValueError("patch reconstructed incorrect target system image")
943
944 print "test of system image patch succeeded"
945
946
Doug Zongkerc8b4e842014-06-16 15:16:31 -0700947class FileDifference:
948 def __init__(self, partition, source_zip, target_zip, output_zip):
949 print "Loading target..."
950 self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
951 print "Loading source..."
952 self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
953
954 self.verbatim_targets = verbatim_targets = []
955 self.patch_list = patch_list = []
956 diffs = []
957 self.renames = renames = {}
958 known_paths = set()
959 largest_source_size = 0
960
961 matching_file_cache = {}
962 for fn, sf in source_data.items():
963 assert fn == sf.name
964 matching_file_cache["path:" + fn] = sf
965 if fn in target_data.keys():
966 AddToKnownPaths(fn, known_paths)
967 # Only allow eligibility for filename/sha matching
968 # if there isn't a perfect path match.
969 if target_data.get(sf.name) is None:
970 matching_file_cache["file:" + fn.split("/")[-1]] = sf
971 matching_file_cache["sha:" + sf.sha1] = sf
972
973 for fn in sorted(target_data.keys()):
974 tf = target_data[fn]
975 assert fn == tf.name
976 sf = ClosestFileMatch(tf, matching_file_cache, renames)
977 if sf is not None and sf.name != tf.name:
978 print "File has moved from " + sf.name + " to " + tf.name
979 renames[sf.name] = tf
980
981 if sf is None or fn in OPTIONS.require_verbatim:
982 # This file should be included verbatim
983 if fn in OPTIONS.prohibit_verbatim:
984 raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
985 print "send", fn, "verbatim"
986 tf.AddToZip(output_zip)
987 verbatim_targets.append((fn, tf.size))
988 if fn in target_data.keys():
989 AddToKnownPaths(fn, known_paths)
990 elif tf.sha1 != sf.sha1:
991 # File is different; consider sending as a patch
992 diffs.append(common.Difference(tf, sf))
993 else:
994 # Target file data identical to source (may still be renamed)
995 pass
996
997 common.ComputeDifferences(diffs)
998
999 for diff in diffs:
1000 tf, sf, d = diff.GetPatch()
1001 path = "/".join(tf.name.split("/")[:-1])
1002 if d is None or len(d) > tf.size * OPTIONS.patch_threshold or \
1003 path not in known_paths:
1004 # patch is almost as big as the file; don't bother patching
1005 # or a patch + rename cannot take place due to the target
1006 # directory not existing
1007 tf.AddToZip(output_zip)
1008 verbatim_targets.append((tf.name, tf.size))
1009 if sf.name in renames:
1010 del renames[sf.name]
1011 AddToKnownPaths(tf.name, known_paths)
1012 else:
1013 common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
1014 patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
1015 largest_source_size = max(largest_source_size, sf.size)
1016
1017 self.largest_source_size = largest_source_size
1018
1019 def EmitVerification(self, script):
1020 so_far = 0
1021 for tf, sf, size, patch_sha in self.patch_list:
1022 if tf.name != sf.name:
1023 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1024 script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
1025 so_far += sf.size
1026 return so_far
1027
1028 def RemoveUnneededFiles(self, script, extras=()):
1029 script.DeleteFiles(["/"+i[0] for i in self.verbatim_targets] +
1030 ["/"+i for i in sorted(self.source_data)
1031 if i not in self.target_data and
1032 i not in self.renames] +
1033 list(extras))
1034
1035 def TotalPatchSize(self):
1036 return sum(i[1].size for i in self.patch_list)
1037
1038 def EmitPatches(self, script, total_patch_size, so_far):
1039 self.deferred_patch_list = deferred_patch_list = []
1040 for item in self.patch_list:
1041 tf, sf, size, _ = item
1042 if tf.name == "system/build.prop":
1043 deferred_patch_list.append(item)
1044 continue
1045 if (sf.name != tf.name):
1046 script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
1047 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1048 so_far += tf.size
1049 script.SetProgress(so_far / total_patch_size)
1050 return so_far
1051
1052 def EmitDeferredPatches(self, script):
1053 for item in self.deferred_patch_list:
1054 tf, sf, size, _ = item
1055 script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1, "patch/"+sf.name+".p")
1056 script.SetPermissions("/system/build.prop", 0, 0, 0644, None, None)
1057
1058 def EmitRenames(self, script):
1059 if len(self.renames) > 0:
1060 script.Print("Renaming files...")
1061 for src, tgt in self.renames.iteritems():
1062 print "Renaming " + src + " to " + tgt.name
1063 script.RenameFile(src, tgt.name)
1064
1065
1066
1067
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001068def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
Geremy Condra36bd3652014-02-06 19:45:10 -08001069 target_has_recovery_patch = HasRecoveryPatch(target_zip)
1070 source_has_recovery_patch = HasRecoveryPatch(source_zip)
1071
Doug Zongker26e66192014-02-20 13:22:07 -08001072 if (OPTIONS.block_based and
1073 target_has_recovery_patch and
1074 source_has_recovery_patch):
Geremy Condra36bd3652014-02-06 19:45:10 -08001075 return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
1076
Doug Zongker37974732010-09-16 17:44:38 -07001077 source_version = OPTIONS.source_info_dict["recovery_api_version"]
1078 target_version = OPTIONS.target_info_dict["recovery_api_version"]
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001079
Doug Zongker9ce2ebf2010-04-21 14:08:44 -07001080 if source_version == 0:
1081 print ("WARNING: generating edify script for a source that "
1082 "can't install it.")
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001083 script = edify_generator.EdifyGenerator(source_version,
1084 OPTIONS.target_info_dict)
Doug Zongkereef39442009-04-02 12:14:19 -07001085
Michael Runge6e836112014-04-15 17:40:21 -07001086 oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
1087 oem_dict = None
1088 if oem_props is not None:
1089 if OPTIONS.oem_source is None:
1090 raise common.ExternalError("OEM source required for this build")
1091 script.Mount("/oem")
1092 oem_dict = common.LoadDictionaryFromLines(open(OPTIONS.oem_source).readlines())
1093
1094 metadata = {"pre-device": GetOemProperty("ro.product.device", oem_props, oem_dict,
Doug Zongker1eb74dd2012-08-16 16:19:00 -07001095 OPTIONS.source_info_dict),
1096 "post-timestamp": GetBuildProp("ro.build.date.utc",
1097 OPTIONS.target_info_dict),
Doug Zongker2ea21062010-04-28 16:05:21 -07001098 }
1099
Doug Zongker05d3dea2009-06-22 11:32:31 -07001100 device_specific = common.DeviceSpecificParams(
1101 source_zip=source_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001102 source_version=source_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001103 target_zip=target_zip,
Doug Zongker14833602010-02-02 13:12:04 -08001104 target_version=target_version,
Doug Zongker05d3dea2009-06-22 11:32:31 -07001105 output_zip=output_zip,
Doug Zongker2ea21062010-04-28 16:05:21 -07001106 script=script,
Doug Zongker96a57e72010-09-26 14:57:41 -07001107 metadata=metadata,
1108 info_dict=OPTIONS.info_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001109
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001110 system_diff = FileDifference("system", source_zip, target_zip, output_zip)
Michael Runge6e836112014-04-15 17:40:21 -07001111 script.Mount("/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001112 if HasVendorPartition(target_zip):
1113 vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
1114 script.Mount("/vendor")
1115 else:
1116 vendor_diff = None
Michael Runge6e836112014-04-15 17:40:21 -07001117
1118 target_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.target_info_dict)
1119 source_fp = CalculateFingerprint(oem_props, oem_dict, OPTIONS.source_info_dict)
1120
1121 if oem_props is None:
1122 script.AssertSomeFingerprint(source_fp, target_fp)
1123 else:
1124 script.AssertSomeThumbprint(
1125 GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
1126 GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
1127
Doug Zongker2ea21062010-04-28 16:05:21 -07001128 metadata["pre-build"] = source_fp
1129 metadata["post-build"] = target_fp
Doug Zongkereef39442009-04-02 12:14:19 -07001130
Doug Zongker55d93282011-01-25 17:03:34 -08001131 source_boot = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001132 "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
1133 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001134 target_boot = common.GetBootableImage(
1135 "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001136 updating_boot = (not OPTIONS.two_step and
1137 (source_boot.data != target_boot.data))
Doug Zongkereef39442009-04-02 12:14:19 -07001138
Doug Zongker55d93282011-01-25 17:03:34 -08001139 source_recovery = common.GetBootableImage(
Doug Zongkerd5131602012-08-02 14:46:42 -07001140 "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
1141 OPTIONS.source_info_dict)
Doug Zongker55d93282011-01-25 17:03:34 -08001142 target_recovery = common.GetBootableImage(
1143 "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
Doug Zongkerf6a8bad2009-05-29 11:41:21 -07001144 updating_recovery = (source_recovery.data != target_recovery.data)
Doug Zongkereef39442009-04-02 12:14:19 -07001145
Doug Zongker881dd402009-09-20 14:03:55 -07001146 # Here's how we divide up the progress bar:
1147 # 0.1 for verifying the start state (PatchCheck calls)
1148 # 0.8 for applying patches (ApplyPatch calls)
1149 # 0.1 for unpacking verbatim files, symlinking, and doing the
1150 # device-specific commands.
Doug Zongkereef39442009-04-02 12:14:19 -07001151
Michael Runge6e836112014-04-15 17:40:21 -07001152 AppendAssertions(script, OPTIONS.target_info_dict, oem_dict)
Doug Zongker05d3dea2009-06-22 11:32:31 -07001153 device_specific.IncrementalOTA_Assertions()
Doug Zongkereef39442009-04-02 12:14:19 -07001154
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001155 # Two-step incremental package strategy (in chronological order,
1156 # which is *not* the order in which the generated script has
1157 # things):
1158 #
1159 # if stage is not "2/3" or "3/3":
1160 # do verification on current system
1161 # write recovery image to boot partition
1162 # set stage to "2/3"
1163 # reboot to boot partition and restart recovery
1164 # else if stage is "2/3":
1165 # write recovery image to recovery partition
1166 # set stage to "3/3"
1167 # reboot to recovery partition and restart recovery
1168 # else:
1169 # (stage must be "3/3")
1170 # perform update:
1171 # patch system files, etc.
1172 # force full install of new boot image
1173 # set up system to update recovery partition on first boot
1174 # complete script normally (allow recovery to mark itself finished and reboot)
1175
1176 if OPTIONS.two_step:
1177 if not OPTIONS.info_dict.get("multistage_support", None):
1178 assert False, "two-step packages not supported by this build"
1179 fs = OPTIONS.info_dict["fstab"]["/misc"]
1180 assert fs.fs_type.upper() == "EMMC", \
1181 "two-step packages only supported on devices with EMMC /misc partitions"
1182 bcb_dev = {"bcb_dev": fs.device}
1183 common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
1184 script.AppendExtra("""
1185if get_stage("%(bcb_dev)s", "stage") == "2/3" then
1186""" % bcb_dev)
1187 script.AppendExtra("sleep(20);\n");
1188 script.WriteRawImage("/recovery", "recovery.img")
1189 script.AppendExtra("""
1190set_stage("%(bcb_dev)s", "3/3");
1191reboot_now("%(bcb_dev)s", "recovery");
1192else if get_stage("%(bcb_dev)s", "stage") != "3/3" then
1193""" % bcb_dev)
1194
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001195 script.Print("Verifying current system...")
1196
Doug Zongkere5ff5902012-01-17 10:55:37 -08001197 device_specific.IncrementalOTA_VerifyBegin()
1198
Doug Zongker881dd402009-09-20 14:03:55 -07001199 script.ShowProgress(0.1, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001200 so_far = system_diff.EmitVerification(script)
1201 if vendor_diff:
1202 so_far += vendor_diff.EmitVerification(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001203
Doug Zongker5da317e2009-06-02 13:38:17 -07001204 if updating_boot:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001205 d = common.Difference(target_boot, source_boot)
Doug Zongker761e6422009-09-25 10:45:39 -07001206 _, _, d = d.ComputePatch()
Doug Zongker5da317e2009-06-02 13:38:17 -07001207 print "boot target: %d source: %d diff: %d" % (
1208 target_boot.size, source_boot.size, len(d))
1209
Doug Zongker048e7ca2009-06-15 14:31:53 -07001210 common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
Doug Zongker5da317e2009-06-02 13:38:17 -07001211
Doug Zongker96a57e72010-09-26 14:57:41 -07001212 boot_type, boot_device = common.GetTypeAndDevice("/boot", OPTIONS.info_dict)
Doug Zongkerf2ab2902010-09-22 10:12:54 -07001213
1214 script.PatchCheck("%s:%s:%d:%s:%d:%s" %
1215 (boot_type, boot_device,
Doug Zongker67369982010-07-07 13:53:32 -07001216 source_boot.size, source_boot.sha1,
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001217 target_boot.size, target_boot.sha1))
Doug Zongker881dd402009-09-20 14:03:55 -07001218 so_far += source_boot.size
Doug Zongker5da317e2009-06-02 13:38:17 -07001219
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001220 size = []
1221 if system_diff.patch_list: size.append(system_diff.largest_source_size)
1222 if vendor_diff:
1223 if vendor_diff.patch_list: size.append(vendor_diff.largest_source_size)
1224 if size or updating_recovery or updating_boot:
1225 script.CacheFreeSpaceCheck(max(size))
Doug Zongker5a482092010-02-17 16:09:18 -08001226
Doug Zongker05d3dea2009-06-22 11:32:31 -07001227 device_specific.IncrementalOTA_VerifyEnd()
1228
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001229 if OPTIONS.two_step:
1230 script.WriteRawImage("/boot", "recovery.img")
1231 script.AppendExtra("""
1232set_stage("%(bcb_dev)s", "2/3");
1233reboot_now("%(bcb_dev)s", "");
1234else
1235""" % bcb_dev)
1236
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001237 script.Comment("---- start making changes here ----")
Doug Zongkereef39442009-04-02 12:14:19 -07001238
Doug Zongkere5ff5902012-01-17 10:55:37 -08001239 device_specific.IncrementalOTA_InstallBegin()
1240
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001241 if OPTIONS.two_step:
1242 common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
1243 script.WriteRawImage("/boot", "boot.img")
1244 print "writing full boot image (forced by two-step mode)"
1245
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001246 script.Print("Removing unneeded files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001247 system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
1248 if vendor_diff:
1249 vendor_diff.RemoveUnneededFiles(script)
Doug Zongkereef39442009-04-02 12:14:19 -07001250
Doug Zongker881dd402009-09-20 14:03:55 -07001251 script.ShowProgress(0.8, 0)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001252 total_patch_size = 1.0 + system_diff.TotalPatchSize()
1253 if vendor_diff:
1254 total_patch_size += vendor_diff.TotalPatchSize()
Doug Zongker881dd402009-09-20 14:03:55 -07001255 if updating_boot:
1256 total_patch_size += target_boot.size
Doug Zongker881dd402009-09-20 14:03:55 -07001257
1258 script.Print("Patching system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001259 so_far = system_diff.EmitPatches(script, total_patch_size, 0)
1260 if vendor_diff:
1261 script.Print("Patching vendor files...")
1262 so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
Doug Zongker881dd402009-09-20 14:03:55 -07001263
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001264 if not OPTIONS.two_step:
1265 if updating_boot:
1266 # Produce the boot image by applying a patch to the current
1267 # contents of the boot partition, and write it back to the
1268 # partition.
1269 script.Print("Patching boot image...")
1270 script.ApplyPatch("%s:%s:%d:%s:%d:%s"
1271 % (boot_type, boot_device,
1272 source_boot.size, source_boot.sha1,
1273 target_boot.size, target_boot.sha1),
1274 "-",
1275 target_boot.size, target_boot.sha1,
1276 source_boot.sha1, "patch/boot.img.p")
1277 so_far += target_boot.size
1278 script.SetProgress(so_far / total_patch_size)
1279 print "boot image changed; including."
1280 else:
1281 print "boot image unchanged; skipping."
Doug Zongkereef39442009-04-02 12:14:19 -07001282
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001283 system_items = ItemSet("system", "META/filesystem_config.txt")
1284 if vendor_diff:
1285 vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
1286
Doug Zongkereef39442009-04-02 12:14:19 -07001287 if updating_recovery:
Doug Zongkerb32161a2012-08-21 10:33:44 -07001288 # Recovery is generated as a patch using both the boot image
1289 # (which contains the same linux kernel as recovery) and the file
1290 # /system/etc/recovery-resource.dat (which contains all the images
1291 # used in the recovery UI) as sources. This lets us minimize the
1292 # size of the patch, which must be included in every OTA package.
Doug Zongker73ef8252009-07-23 15:12:53 -07001293 #
Doug Zongkerb32161a2012-08-21 10:33:44 -07001294 # For older builds where recovery-resource.dat is not present, we
1295 # use only the boot image as the source.
1296
Doug Zongkerc9253822014-02-04 12:17:58 -08001297 if not target_has_recovery_patch:
1298 def output_sink(fn, data):
1299 common.ZipWriteStr(output_zip, "recovery/" + fn, data)
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001300 system_items.Get("system/" + fn, dir=False)
Doug Zongkerc9253822014-02-04 12:17:58 -08001301
1302 common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
1303 target_recovery, target_boot)
1304 script.DeleteFiles(["/system/recovery-from-boot.p",
1305 "/system/etc/install-recovery.sh"])
Doug Zongker73ef8252009-07-23 15:12:53 -07001306 print "recovery image changed; including as patch from boot."
Doug Zongkereef39442009-04-02 12:14:19 -07001307 else:
1308 print "recovery image unchanged; skipping."
1309
Doug Zongker881dd402009-09-20 14:03:55 -07001310 script.ShowProgress(0.1, 10)
Doug Zongkereef39442009-04-02 12:14:19 -07001311
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001312 target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
1313 if vendor_diff:
1314 target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
1315
1316 temp_script = script.MakeTemporary()
1317 system_items.GetMetadata(target_zip)
1318 system_items.Get("system").SetPermissions(temp_script)
1319 if vendor_diff:
1320 vendor_items.GetMetadata(target_zip)
1321 vendor_items.Get("vendor").SetPermissions(temp_script)
1322
1323 # Note that this call will mess up the trees of Items, so make sure
1324 # we're done with them.
1325 source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
1326 if vendor_diff:
1327 source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
Doug Zongkereef39442009-04-02 12:14:19 -07001328
1329 target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
Doug Zongkereef39442009-04-02 12:14:19 -07001330 source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
1331
1332 # Delete all the symlinks in source that aren't in target. This
1333 # needs to happen before verbatim files are unpacked, in case a
1334 # symlink in the source is replaced by a real file in the target.
1335 to_delete = []
1336 for dest, link in source_symlinks:
1337 if link not in target_symlinks_d:
1338 to_delete.append(link)
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001339 script.DeleteFiles(to_delete)
Doug Zongkereef39442009-04-02 12:14:19 -07001340
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001341 if system_diff.verbatim_targets:
1342 script.Print("Unpacking new system files...")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001343 script.UnpackPackageDir("system", "/system")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001344 if vendor_diff and vendor_diff.verbatim_targets:
1345 script.Print("Unpacking new vendor files...")
1346 script.UnpackPackageDir("vendor", "/vendor")
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001347
Doug Zongkerc9253822014-02-04 12:17:58 -08001348 if updating_recovery and not target_has_recovery_patch:
Doug Zongker42265392010-02-12 10:21:00 -08001349 script.Print("Unpacking new recovery...")
1350 script.UnpackPackageDir("recovery", "/system")
1351
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001352 system_diff.EmitRenames(script)
1353 if vendor_diff:
1354 vendor_diff.EmitRenames(script)
Michael Runge4038aa82013-12-13 18:06:28 -08001355
Doug Zongker05d3dea2009-06-22 11:32:31 -07001356 script.Print("Symlinks and permissions...")
Doug Zongkereef39442009-04-02 12:14:19 -07001357
1358 # Create all the symlinks that don't already exist, or point to
1359 # somewhere different than what we want. Delete each symlink before
1360 # creating it, since the 'symlink' command won't overwrite.
1361 to_create = []
1362 for dest, link in target_symlinks:
1363 if link in source_symlinks_d:
1364 if dest != source_symlinks_d[link]:
1365 to_create.append((dest, link))
1366 else:
1367 to_create.append((dest, link))
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001368 script.DeleteFiles([i[1] for i in to_create])
1369 script.MakeSymlinks(to_create)
Doug Zongkereef39442009-04-02 12:14:19 -07001370
1371 # Now that the symlinks are created, we can set all the
1372 # permissions.
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001373 script.AppendScript(temp_script)
Doug Zongkereef39442009-04-02 12:14:19 -07001374
Doug Zongker881dd402009-09-20 14:03:55 -07001375 # Do device-specific installation (eg, write radio image).
Doug Zongker05d3dea2009-06-22 11:32:31 -07001376 device_specific.IncrementalOTA_InstallEnd()
1377
Doug Zongker1c390a22009-05-14 19:06:36 -07001378 if OPTIONS.extra_script is not None:
Doug Zongker67369982010-07-07 13:53:32 -07001379 script.AppendExtra(OPTIONS.extra_script)
Doug Zongker1c390a22009-05-14 19:06:36 -07001380
Doug Zongkere92f15a2011-08-26 13:46:40 -07001381 # Patch the build.prop file last, so if something fails but the
1382 # device can still come up, it appears to be the old build and will
1383 # get set the OTA package again to retry.
1384 script.Print("Patching remaining system files...")
Doug Zongkerc8b4e842014-06-16 15:16:31 -07001385 system_diff.EmitDeferredPatches(script)
Doug Zongkere92f15a2011-08-26 13:46:40 -07001386
Doug Zongker922206e2014-03-04 13:16:24 -08001387 if OPTIONS.wipe_user_data:
1388 script.Print("Erasing user data...")
1389 script.FormatPartition("/data")
1390
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001391 if OPTIONS.two_step:
1392 script.AppendExtra("""
1393set_stage("%(bcb_dev)s", "");
1394endif;
1395endif;
1396""" % bcb_dev)
1397
Doug Zongker25568482014-03-03 10:21:27 -08001398 script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
Doug Zongker2ea21062010-04-28 16:05:21 -07001399 WriteMetadata(metadata, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001400
1401
1402def main(argv):
1403
1404 def option_handler(o, a):
Doug Zongker25568482014-03-03 10:21:27 -08001405 if o == "--board_config":
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001406 pass # deprecated
Doug Zongkereef39442009-04-02 12:14:19 -07001407 elif o in ("-k", "--package_key"):
1408 OPTIONS.package_key = a
Doug Zongkereef39442009-04-02 12:14:19 -07001409 elif o in ("-i", "--incremental_from"):
1410 OPTIONS.incremental_source = a
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001411 elif o in ("-w", "--wipe_user_data"):
1412 OPTIONS.wipe_user_data = True
Doug Zongker962069c2009-04-23 11:41:58 -07001413 elif o in ("-n", "--no_prereq"):
1414 OPTIONS.omit_prereq = True
Michael Runge6e836112014-04-15 17:40:21 -07001415 elif o in ("-o", "--oem_settings"):
1416 OPTIONS.oem_source = a
Doug Zongker1c390a22009-05-14 19:06:36 -07001417 elif o in ("-e", "--extra_script"):
1418 OPTIONS.extra_script = a
Hristo Bojinovdafb0422010-08-26 14:35:16 -07001419 elif o in ("-a", "--aslr_mode"):
1420 if a in ("on", "On", "true", "True", "yes", "Yes"):
1421 OPTIONS.aslr_mode = True
1422 else:
1423 OPTIONS.aslr_mode = False
Doug Zongker761e6422009-09-25 10:45:39 -07001424 elif o in ("--worker_threads"):
1425 OPTIONS.worker_threads = int(a)
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001426 elif o in ("-2", "--two_step"):
1427 OPTIONS.two_step = True
Doug Zongker26e66192014-02-20 13:22:07 -08001428 elif o == "--no_signing":
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001429 OPTIONS.no_signing = True
Doug Zongker26e66192014-02-20 13:22:07 -08001430 elif o == "--block":
1431 OPTIONS.block_based = True
Doug Zongker25568482014-03-03 10:21:27 -08001432 elif o in ("-b", "--binary"):
1433 OPTIONS.updater_binary = a
Doug Zongkereef39442009-04-02 12:14:19 -07001434 else:
1435 return False
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001436 return True
Doug Zongkereef39442009-04-02 12:14:19 -07001437
1438 args = common.ParseOptions(argv, __doc__,
Michael Runge6e836112014-04-15 17:40:21 -07001439 extra_opts="b:k:i:d:wne:a:2o:",
Doug Zongkereef39442009-04-02 12:14:19 -07001440 extra_long_opts=["board_config=",
1441 "package_key=",
Doug Zongkerdbfaae52009-04-21 17:12:54 -07001442 "incremental_from=",
Doug Zongker962069c2009-04-23 11:41:58 -07001443 "wipe_user_data",
Doug Zongker1c390a22009-05-14 19:06:36 -07001444 "no_prereq",
Doug Zongkerc494d7c2009-06-18 08:43:44 -07001445 "extra_script=",
Hristo Bojinov96be7202010-08-02 10:26:17 -07001446 "worker_threads=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001447 "aslr_mode=",
Doug Zongker9b23f2c2013-11-25 14:44:12 -08001448 "two_step",
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001449 "no_signing",
Doug Zongker26e66192014-02-20 13:22:07 -08001450 "block",
Doug Zongker25568482014-03-03 10:21:27 -08001451 "binary=",
Michael Runge6e836112014-04-15 17:40:21 -07001452 "oem_settings=",
Doug Zongkerc60c1ba2010-09-03 13:22:38 -07001453 ],
Doug Zongkereef39442009-04-02 12:14:19 -07001454 extra_option_handler=option_handler)
1455
1456 if len(args) != 2:
1457 common.Usage(__doc__)
1458 sys.exit(1)
1459
Doug Zongker1c390a22009-05-14 19:06:36 -07001460 if OPTIONS.extra_script is not None:
1461 OPTIONS.extra_script = open(OPTIONS.extra_script).read()
1462
Doug Zongkereef39442009-04-02 12:14:19 -07001463 print "unzipping target target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001464 OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
Doug Zongkerfdd8e692009-08-03 17:27:48 -07001465
Doug Zongkereef39442009-04-02 12:14:19 -07001466 OPTIONS.target_tmp = OPTIONS.input_tmp
Doug Zongker37974732010-09-16 17:44:38 -07001467 OPTIONS.info_dict = common.LoadInfoDict(input_zip)
Kenny Roote2e9f612013-05-29 12:59:35 -07001468
1469 # If this image was originally labelled with SELinux contexts, make sure we
1470 # also apply the labels in our new image. During building, the "file_contexts"
1471 # is in the out/ directory tree, but for repacking from target-files.zip it's
1472 # in the root directory of the ramdisk.
1473 if "selinux_fc" in OPTIONS.info_dict:
1474 OPTIONS.info_dict["selinux_fc"] = os.path.join(OPTIONS.input_tmp, "BOOT", "RAMDISK",
1475 "file_contexts")
1476
Doug Zongker37974732010-09-16 17:44:38 -07001477 if OPTIONS.verbose:
1478 print "--- target info ---"
1479 common.DumpInfoDict(OPTIONS.info_dict)
1480
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001481 # If the caller explicitly specified the device-specific extensions
1482 # path via -s/--device_specific, use that. Otherwise, use
1483 # META/releasetools.py if it is present in the target target_files.
1484 # Otherwise, take the path of the file from 'tool_extensions' in the
1485 # info dict and look for that in the local filesystem, relative to
1486 # the current directory.
1487
Doug Zongker37974732010-09-16 17:44:38 -07001488 if OPTIONS.device_specific is None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001489 from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
1490 if os.path.exists(from_input):
1491 print "(using device-specific extensions from target_files)"
1492 OPTIONS.device_specific = from_input
1493 else:
1494 OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
1495
Doug Zongker37974732010-09-16 17:44:38 -07001496 if OPTIONS.device_specific is not None:
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001497 OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
Doug Zongker37974732010-09-16 17:44:38 -07001498
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001499 if OPTIONS.no_signing:
1500 output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED)
1501 else:
1502 temp_zip_file = tempfile.NamedTemporaryFile()
1503 output_zip = zipfile.ZipFile(temp_zip_file, "w",
1504 compression=zipfile.ZIP_DEFLATED)
Doug Zongkereef39442009-04-02 12:14:19 -07001505
1506 if OPTIONS.incremental_source is None:
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001507 WriteFullOTAPackage(input_zip, output_zip)
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001508 if OPTIONS.package_key is None:
1509 OPTIONS.package_key = OPTIONS.info_dict.get(
1510 "default_system_dev_certificate",
1511 "build/target/product/security/testkey")
Doug Zongkereef39442009-04-02 12:14:19 -07001512 else:
1513 print "unzipping source target-files..."
Doug Zongker55d93282011-01-25 17:03:34 -08001514 OPTIONS.source_tmp, source_zip = common.UnzipTemp(OPTIONS.incremental_source)
Doug Zongker37974732010-09-16 17:44:38 -07001515 OPTIONS.target_info_dict = OPTIONS.info_dict
1516 OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
Doug Zongker5fad2032014-02-24 08:13:45 -08001517 if "selinux_fc" in OPTIONS.source_info_dict:
1518 OPTIONS.source_info_dict["selinux_fc"] = os.path.join(OPTIONS.source_tmp, "BOOT", "RAMDISK",
1519 "file_contexts")
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001520 if OPTIONS.package_key is None:
Doug Zongker91b4f8a2011-09-23 12:48:33 -07001521 OPTIONS.package_key = OPTIONS.source_info_dict.get(
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001522 "default_system_dev_certificate",
1523 "build/target/product/security/testkey")
Doug Zongker37974732010-09-16 17:44:38 -07001524 if OPTIONS.verbose:
1525 print "--- source info ---"
1526 common.DumpInfoDict(OPTIONS.source_info_dict)
Doug Zongkerc77a9ad2010-09-16 11:28:43 -07001527 WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
Doug Zongkereef39442009-04-02 12:14:19 -07001528
1529 output_zip.close()
Doug Zongkerafb32ea2011-09-22 10:28:04 -07001530
Takeshi Kanemotoe153b342013-11-14 17:20:50 +09001531 if not OPTIONS.no_signing:
1532 SignOutput(temp_zip_file.name, args[1])
1533 temp_zip_file.close()
Doug Zongkereef39442009-04-02 12:14:19 -07001534
1535 common.Cleanup()
1536
1537 print "done."
1538
1539
1540if __name__ == '__main__':
1541 try:
Ying Wang7e6d4e42010-12-13 16:25:36 -08001542 common.CloseInheritedPipes()
Doug Zongkereef39442009-04-02 12:14:19 -07001543 main(sys.argv[1:])
1544 except common.ExternalError, e:
1545 print
1546 print " ERROR: %s" % (e,)
1547 print
1548 sys.exit(1)